mirror of
https://github.com/AssetRipper/AssetRipper.TextureDecoder.git
synced 2026-11-12 18:07:28 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2772dcb83 | ||
|
|
1e363e355e | ||
|
|
8e82e702a7 | ||
|
|
1957784de4 | ||
|
|
1bf5f70ead | ||
|
|
2bb946b26f | ||
|
|
eed528cc68 | ||
|
|
c6d3236770 | ||
|
|
34d8310764 | ||
|
|
e7b9db0dd6 | ||
|
|
1e7ef0c44c | ||
|
|
0f2a6fdec1 | ||
|
|
058a14953f | ||
|
|
94f1a9ab8e | ||
|
|
5953a2baf7 | ||
|
|
4b398e6c94 | ||
|
|
a25a42dbd4 | ||
|
|
e9674d1c39 | ||
|
|
05eaeb81dc | ||
|
|
76a8b2f837 | ||
|
|
c113649b6e | ||
|
|
07002c77ee | ||
|
|
bed45ee60e | ||
|
|
d8918771a9 | ||
|
|
5dba4cdb19 | ||
|
|
b1803e8a35 | ||
|
|
5ec29c6a82 | ||
|
|
c6a1240499 | ||
|
|
12c6750a88 | ||
|
|
5dacd489d0 | ||
|
|
509a567aa5 | ||
|
|
1f1dd55e85 | ||
|
|
5e28554f00 | ||
|
|
d4cd1d5560 | ||
|
|
9ae8296532 | ||
|
|
637a53fc6f | ||
|
|
53f177cae3 | ||
|
|
c57303ff6b | ||
|
|
bd834212ad | ||
|
|
f0bdada27c | ||
|
|
4a386d57d7 | ||
|
|
d96d8e6c35 | ||
|
|
c16bec4431 | ||
|
|
548d5e8aec | ||
|
|
9e21272b2b | ||
|
|
df1ab3c1e4 | ||
|
|
7c30eb44ee | ||
|
|
e32f934043 |
No files matched your search
@@ -14,9 +14,9 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
uses: actions/setup-dotnet@v4.0.0
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
dotnet-version: 8.0.x
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
|
||||
@@ -2,13 +2,10 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.8" />
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.12" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
-3
@@ -2,9 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -42,8 +42,11 @@ internal static class NumericConversionGenerator
|
||||
{
|
||||
foreach ((CSharpPrimitives.Data unsignedType, CSharpPrimitives.Data signedType) in GetIntegerTypes())
|
||||
{
|
||||
WriteMethod(writer, unsignedType, signedType, unsignedType);
|
||||
WriteMethod(writer, signedType, unsignedType, unsignedType);
|
||||
if (!unsignedType.HasPointerSize(out _, out _))
|
||||
{
|
||||
WriteMethod(writer, unsignedType, signedType, unsignedType);
|
||||
WriteMethod(writer, signedType, unsignedType, unsignedType);
|
||||
}
|
||||
}
|
||||
|
||||
static void WriteMethod(IndentedTextWriter writer, CSharpPrimitives.Data parameterType, CSharpPrimitives.Data returnType, CSharpPrimitives.Data unsignedType)
|
||||
@@ -105,10 +108,20 @@ internal static class NumericConversionGenerator
|
||||
writer.WriteLine($"private static TTo {methodName}<TTo>({from.LangName} value) where TTo : unmanaged");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
if (from.IsSignedInteger(out CSharpPrimitives.Data? unsignedFromData))
|
||||
if (from.HasPointerSize(out CSharpPrimitives.Data? bit32From, out CSharpPrimitives.Data? bit64From))
|
||||
{
|
||||
writer.WriteLine($"{unsignedFromData.LangName} unsigned = {ChangeSign}(value);");
|
||||
writer.WriteLine($"return {ConvertMethodName(unsignedFromData.Type)}<TTo>(unsigned);");
|
||||
using (new If(writer, "IntPtr.Size == sizeof(int)"))
|
||||
{
|
||||
writer.WriteLine($"return {ConvertMethodName(bit32From.Type)}<TTo>(({bit32From.LangName})value);");
|
||||
}
|
||||
using (new Else(writer))
|
||||
{
|
||||
writer.WriteLine($"return {ConvertMethodName(bit64From.Type)}<TTo>(({bit64From.LangName})value);");
|
||||
}
|
||||
}
|
||||
else if (from.IsSignedInteger(out CSharpPrimitives.Data? unsignedFrom))
|
||||
{
|
||||
writer.WriteLine($"return {ConvertMethodName(unsignedFrom.Type)}<TTo>({ChangeSign}(value));");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -122,6 +135,19 @@ internal static class NumericConversionGenerator
|
||||
{
|
||||
writer.WriteLine($"return Unsafe.As<{to.LangName}, TTo>(ref 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);");
|
||||
}
|
||||
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);");
|
||||
}
|
||||
}
|
||||
else if (to.IsSignedInteger(out CSharpPrimitives.Data? unsignedTo))
|
||||
{
|
||||
writer.WriteLine($"{to.LangName} converted = {ChangeSign}({methodName}<{unsignedTo.LangName}>(value));");
|
||||
|
||||
@@ -7,9 +7,10 @@ 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
|
||||
@@ -46,6 +47,10 @@ internal static partial class Program
|
||||
{
|
||||
WriteGenericColor(genericColor);
|
||||
}
|
||||
for (int i = 1; i <= 4; i++)
|
||||
{
|
||||
WriteSuperGenericColor(i);
|
||||
}
|
||||
NumericConversionGenerator.Run();
|
||||
Console.WriteLine("Done!");
|
||||
}
|
||||
@@ -54,7 +59,7 @@ internal static partial class Program
|
||||
{
|
||||
(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);
|
||||
}
|
||||
|
||||
@@ -62,21 +67,29 @@ internal static partial class Program
|
||||
{
|
||||
(string name, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha) = details;
|
||||
Console.WriteLine(name);
|
||||
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(OutputFolder, name);
|
||||
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(FormatsFolder, name);
|
||||
WriteGenericColor(writer, name, hasRed, hasGreen, hasBlue, hasAlpha);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
string typeName = CSharpPrimitives.Dictionary[type].LangName;
|
||||
writer.WriteGeneratedCodeWarning();
|
||||
writer.WriteLine();
|
||||
using (new Namespace(writer, OutputNamespace))
|
||||
using (new Namespace(writer, FormatsNamespace))
|
||||
{
|
||||
writer.WriteLine($"public partial struct {name} : IColor<{typeName}>");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
WriteColorBaseStaticProperties(writer, hasRed, hasGreen, hasBlue, hasAlpha, fullyUtilized, typeName);
|
||||
WriteNonGenericStaticProperties(writer, hasRed, hasGreen, hasBlue, hasAlpha, fullyUtilized, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteToString(writer, hasRed, hasGreen, hasBlue, hasAlpha);
|
||||
}
|
||||
@@ -127,32 +140,93 @@ internal static partial class Program
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteColorBaseStaticProperties(IndentedTextWriter writer, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, bool fullyUtilized, string typeName)
|
||||
private static void WriteNonGenericStaticProperties(IndentedTextWriter writer, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, bool fullyUtilized, string typeName)
|
||||
{
|
||||
writer.WriteLine($"static bool IColorBase.HasRedChannel => {hasRed.ToLowerString()};");
|
||||
writer.WriteLine($"static bool IColorBase.HasGreenChannel => {hasGreen.ToLowerString()};");
|
||||
writer.WriteLine($"static bool IColorBase.HasBlueChannel => {hasBlue.ToLowerString()};");
|
||||
writer.WriteLine($"static bool IColorBase.HasAlphaChannel => {hasAlpha.ToLowerString()};");
|
||||
writer.WriteLine($"static bool IColorBase.ChannelsAreFullyUtilized => {fullyUtilized.ToLowerString()};");
|
||||
writer.WriteLine($"static Type IColorBase.ChannelType => typeof({typeName});");
|
||||
WriteNonGenericStaticProperties(writer, hasRed.ToLowerString(), hasGreen.ToLowerString(), hasBlue.ToLowerString(), hasAlpha.ToLowerString(), fullyUtilized.ToLowerString(), typeName);
|
||||
}
|
||||
|
||||
private static void WriteNonGenericStaticProperties(IndentedTextWriter writer, string hasRed, string hasGreen, string hasBlue, string hasAlpha, string fullyUtilized, string typeName)
|
||||
{
|
||||
writer.WriteLine($"static bool IColor.HasRedChannel => {hasRed};");
|
||||
writer.WriteLine($"static bool IColor.HasGreenChannel => {hasGreen};");
|
||||
writer.WriteLine($"static bool IColor.HasBlueChannel => {hasBlue};");
|
||||
writer.WriteLine($"static bool IColor.HasAlphaChannel => {hasAlpha};");
|
||||
writer.WriteLine($"static bool IColor.ChannelsAreFullyUtilized => {fullyUtilized};");
|
||||
writer.WriteLine($"static Type IColor.ChannelType => typeof({typeName});");
|
||||
}
|
||||
|
||||
private static void WriteBlackWhiteStaticProperties(IndentedTextWriter writer, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, string colorTypeName, string channelTypeName)
|
||||
{
|
||||
string minValue = $"NumericConversion.GetMinimumValueSafe<{channelTypeName}>()";
|
||||
string maxValue = $"NumericConversion.GetMaximumValueSafe<{channelTypeName}>()";
|
||||
|
||||
//Black
|
||||
{
|
||||
writer.Write($"public static {colorTypeName}<{channelTypeName}> Black => new(");
|
||||
int rgbCount = CountRGB(hasRed, hasGreen, hasBlue);
|
||||
for (int i = 0; i < rgbCount; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
writer.Write(", ");
|
||||
}
|
||||
writer.Write(minValue);
|
||||
}
|
||||
if (hasAlpha)
|
||||
{
|
||||
if (rgbCount != 0)
|
||||
{
|
||||
writer.Write(", ");
|
||||
}
|
||||
writer.Write(maxValue);
|
||||
}
|
||||
writer.WriteLine(");");
|
||||
}
|
||||
|
||||
//White
|
||||
if (hasRed || hasGreen || hasBlue)
|
||||
{
|
||||
writer.Write($"public static {colorTypeName}<{channelTypeName}> White => new(");
|
||||
int rgbaCount = CountRGBA(hasRed, hasGreen, hasBlue, hasAlpha);
|
||||
for (int i = 0; i < rgbaCount; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
writer.Write(", ");
|
||||
}
|
||||
writer.Write(maxValue);
|
||||
}
|
||||
writer.WriteLine(");");
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteLine($"static {colorTypeName}<{channelTypeName}> IColor<{colorTypeName}<{channelTypeName}>, {channelTypeName}>.White => throw new NotSupportedException();");
|
||||
}
|
||||
|
||||
static int CountRGB(bool hasRed, bool hasGreen, bool hasBlue)
|
||||
{
|
||||
return (hasRed ? 1 : 0) + (hasGreen ? 1 : 0) + (hasBlue ? 1 : 0);
|
||||
}
|
||||
|
||||
static int CountRGBA(bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha)
|
||||
{
|
||||
return CountRGB(hasRed, hasGreen, hasBlue) + (hasAlpha ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteGenericColor(IndentedTextWriter writer, string name, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha)
|
||||
{
|
||||
const string typeName = "T";
|
||||
const string minValue = $"NumericConversion.GetMinimumValueSafe<T>()";
|
||||
const string maxValue = $"NumericConversion.GetMaximumValueSafe<T>()";
|
||||
const string minValue = "NumericConversion.GetMinimumValueSafe<T>()";
|
||||
const string maxValue = "NumericConversion.GetMaximumValueSafe<T>()";
|
||||
|
||||
writer.WriteGeneratedCodeWarning();
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
writer.WriteFileScopedNamespace(OutputNamespace);
|
||||
writer.WriteFileScopedNamespace(FormatsNamespace);
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
string constraints = hasRed && hasGreen && hasBlue && hasAlpha
|
||||
? "unmanaged"
|
||||
: "unmanaged, INumberBase<T>, IMinMaxValue<T>";
|
||||
writer.WriteLine($"public partial struct {name}<T> : IColor<{typeName}> where T : {constraints}");
|
||||
writer.WriteLine($"public partial struct {name}<T> : IColor<{name}<T>, T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
WriteProperty(writer, hasRed, typeName, 'R', minValue);
|
||||
@@ -164,16 +238,232 @@ internal static partial class Program
|
||||
WriteProperty(writer, hasAlpha, typeName, 'A', maxValue);
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
WriteConstructor(writer, name, hasRed, hasGreen, hasBlue, hasAlpha, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteGetChannels(writer, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteSetChannels(writer, typeName, hasRed, hasGreen, hasBlue, hasAlpha);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteColorBaseStaticProperties(writer, hasRed, hasGreen, hasBlue, hasAlpha, true, typeName);
|
||||
WriteNonGenericStaticProperties(writer, hasRed, hasGreen, hasBlue, hasAlpha, true, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteBlackWhiteStaticProperties(writer, hasRed, hasGreen, hasBlue, hasAlpha, name, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteToString(writer, hasRed, hasGreen, hasBlue, hasAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteSuperGenericColor(IndentedTextWriter writer, int channelCount)
|
||||
{
|
||||
const string typeName = "TChannelValue";
|
||||
const string minValue = $"NumericConversion.GetMinimumValueSafe<{typeName}>()";
|
||||
const string maxValue = $"NumericConversion.GetMaximumValueSafe<{typeName}>()";
|
||||
|
||||
string[] channelTypeParameters = channelCount switch
|
||||
{
|
||||
1 => ["TChannel"],
|
||||
2 => ["TChannel1", "TChannel2"],
|
||||
3 => ["TChannel1", "TChannel2", "TChannel3"],
|
||||
4 => ["TChannel1", "TChannel2", "TChannel3", "TChannel4"],
|
||||
_ => throw new(),
|
||||
};
|
||||
|
||||
string[] fieldNames = channelCount switch
|
||||
{
|
||||
1 => ["value"],
|
||||
2 => ["value1", "value2"],
|
||||
3 => ["value1", "value2", "value3"],
|
||||
4 => ["value1", "value2", "value3", "value4"],
|
||||
_ => throw new(),
|
||||
};
|
||||
|
||||
|
||||
|
||||
writer.WriteGeneratedCodeWarning();
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
writer.WriteUsing("AssetRipper.TextureDecoder.Rgb.Channels");
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
writer.WriteFileScopedNamespace("AssetRipper.TextureDecoder.Rgb");
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
string structName;
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.Append("Color<");
|
||||
sb.Append(typeName);
|
||||
for (int i = 0; i < channelTypeParameters.Length; i++)
|
||||
{
|
||||
sb.Append(", ");
|
||||
sb.Append(channelTypeParameters[i]);
|
||||
}
|
||||
sb.Append('>');
|
||||
structName = sb.ToString();
|
||||
}
|
||||
|
||||
writer.WriteLine($"public partial struct {structName} : IColor<{structName}, {typeName}>");
|
||||
using (new Indented(writer))
|
||||
{
|
||||
writer.WriteLine($"where {typeName} : unmanaged, INumberBase<{typeName}>, IMinMaxValue<{typeName}>");
|
||||
foreach (string channel in channelTypeParameters)
|
||||
{
|
||||
writer.WriteLine($"where {channel} : IChannel");
|
||||
}
|
||||
}
|
||||
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
foreach (string fieldName in fieldNames)
|
||||
{
|
||||
writer.WriteLine($"private {typeName} {fieldName};");
|
||||
}
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
WriteProperty(writer, 'R', "Red", channelTypeParameters, fieldNames, minValue, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteProperty(writer, 'G', "Green", channelTypeParameters, fieldNames, minValue, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteProperty(writer, 'B', "Blue", channelTypeParameters, fieldNames, minValue, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteProperty(writer, 'A', "Alpha", channelTypeParameters, fieldNames, maxValue, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
writer.Write("public Color(");
|
||||
for (int i = 0; i < fieldNames.Length; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
writer.Write(", ");
|
||||
}
|
||||
|
||||
writer.Write(typeName);
|
||||
writer.Write(' ');
|
||||
writer.Write(fieldNames[i]);
|
||||
}
|
||||
writer.WriteLine(')');
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
foreach (string fieldName in fieldNames)
|
||||
{
|
||||
writer.WriteLine($"this.{fieldName} = {fieldName};");
|
||||
}
|
||||
}
|
||||
writer.WriteLineNoTabs();
|
||||
WriteGetChannels(writer, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteSetChannels(writer, typeName, true, true, true, true);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteNonGenericStaticProperties(
|
||||
writer,
|
||||
string.Join(" || ", channelTypeParameters.Select(c => $"{c}.IsRed")),
|
||||
string.Join(" || ", channelTypeParameters.Select(c => $"{c}.IsGreen")),
|
||||
string.Join(" || ", channelTypeParameters.Select(c => $"{c}.IsBlue")),
|
||||
string.Join(" || ", channelTypeParameters.Select(c => $"{c}.IsAlpha")),
|
||||
string.Join(" && ", channelTypeParameters.Select(c => $"{c}.FullyUtilized")),
|
||||
typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
writer.WriteLine($"public static {structName} Black");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
writer.WriteLine($"get => new({string.Join(", ", channelTypeParameters.Select(c => $"{c}.GetBlack<{typeName}>()"))});");
|
||||
}
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
writer.WriteLine($"public static {structName} White");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
writer.WriteLine($"get => new({string.Join(", ", channelTypeParameters.Select(c => $"{c}.GetWhite<{typeName}>()"))});");
|
||||
}
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
WriteToString(writer, true, true, true, true);
|
||||
}
|
||||
|
||||
static void WriteProperty(IndentedTextWriter writer, char property, string channel, string[] channels, string[] fields, string defaultValue, string typeName)
|
||||
{
|
||||
writer.WriteLine($"public {typeName} {property}");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
writer.WriteLine("readonly get");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
If.Write(
|
||||
writer,
|
||||
Enumerable.Range(0, channels.Length).Select(i => new ValueTuple<string, Action<IndentedTextWriter>?>($"{channels[i]}.Is{channel}", (writer) =>
|
||||
{
|
||||
writer.WriteLine($"return {channels[i]}.Get{channel}({fields[i]});");
|
||||
})),
|
||||
(writer) =>
|
||||
{
|
||||
writer.WriteLine($"return {defaultValue};");
|
||||
});
|
||||
}
|
||||
writer.WriteLine("set");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
for (int i = 0; i < channels.Length; i++)
|
||||
{
|
||||
writer.WriteLine($"Channel.SetIf{channel}<{channels[i]}, {typeName}>(ref {fields[i]}, value);");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteConstructor(IndentedTextWriter writer, string declaringTypeName, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, string channelTypeName)
|
||||
{
|
||||
writer.Write($"public {declaringTypeName}(");
|
||||
if (hasRed)
|
||||
{
|
||||
writer.Write($"{channelTypeName} r");
|
||||
if (hasGreen || hasBlue || hasAlpha)
|
||||
{
|
||||
writer.Write(", ");
|
||||
}
|
||||
}
|
||||
if (hasGreen)
|
||||
{
|
||||
writer.Write($"{channelTypeName} g");
|
||||
if (hasBlue || hasAlpha)
|
||||
{
|
||||
writer.Write(", ");
|
||||
}
|
||||
}
|
||||
if (hasBlue)
|
||||
{
|
||||
writer.Write($"{channelTypeName} b");
|
||||
if (hasAlpha)
|
||||
{
|
||||
writer.Write(", ");
|
||||
}
|
||||
}
|
||||
if (hasAlpha)
|
||||
{
|
||||
writer.Write($"{channelTypeName} a");
|
||||
}
|
||||
writer.WriteLine(")");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
if (hasRed)
|
||||
{
|
||||
writer.WriteLine("R = r;");
|
||||
}
|
||||
if (hasGreen)
|
||||
{
|
||||
writer.WriteLine("G = g;");
|
||||
}
|
||||
if (hasBlue)
|
||||
{
|
||||
writer.WriteLine("B = b;");
|
||||
}
|
||||
if (hasAlpha)
|
||||
{
|
||||
writer.WriteLine("A = a;");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteGetChannels(IndentedTextWriter writer, string typeName)
|
||||
{
|
||||
writer.WriteLine($"public readonly void GetChannels(out {typeName} r, out {typeName} g, out {typeName} b, out {typeName} a)");
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<PublishTrimmed>true</PublishTrimmed>
|
||||
<PublishAot>true</PublishAot>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using AssetRipper.TextureDecoder.Exr;
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using StbImageWriteSharp;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -54,6 +56,12 @@ public readonly struct DirectBitmap<TColor, TColorArg>
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveAsExr(string path)
|
||||
{
|
||||
using Stream stream = File.OpenWrite(path);
|
||||
ExrWriter.Write<TColor, TColorArg>(stream, Width, Height, Pixels);
|
||||
}
|
||||
|
||||
public void SaveAsHdr(string path)
|
||||
{
|
||||
GetDataAndComponentsForSaving(out byte[] data, out ColorComponents components);
|
||||
@@ -96,24 +104,34 @@ public readonly struct DirectBitmap<TColor, TColorArg>
|
||||
|
||||
private void GetDataAndComponentsForSaving(out byte[] data, out ColorComponents components)
|
||||
{
|
||||
if (typeof(TColor) == typeof(ColorRGBA<byte>))
|
||||
if (typeof(TColor) == typeof(Color<byte, R, G, B, A>) || typeof(TColor) == typeof(ColorRGBA<byte>))
|
||||
{
|
||||
data = Data;
|
||||
components = ColorComponents.RedGreenBlueAlpha;
|
||||
}
|
||||
else if (typeof(TColor) == typeof(ColorRGB<byte>))
|
||||
else if (typeof(TColor) == typeof(Color<byte, R, G, B>) || typeof(TColor) == typeof(ColorRGB<byte>))
|
||||
{
|
||||
data = Data;
|
||||
components = ColorComponents.RedGreenBlue;
|
||||
}
|
||||
else if (typeof(TColor) == typeof(Color<byte, Gray, A>))
|
||||
{
|
||||
data = Data;
|
||||
components = ColorComponents.GreyAlpha;
|
||||
}
|
||||
else if (typeof(TColor) == typeof(Color<byte, Gray>))
|
||||
{
|
||||
data = Data;
|
||||
components = ColorComponents.Grey;
|
||||
}
|
||||
else if (TColor.HasAlphaChannel)
|
||||
{
|
||||
RgbConverter.Convert<TColor, TColorArg, ColorRGBA<byte>, byte>(Bits, Width, Height, out data);
|
||||
RgbConverter.Convert<TColor, TColorArg, Color<byte, R, G, B, A>, byte>(Bits, Width, Height, out data);
|
||||
components = ColorComponents.RedGreenBlueAlpha;
|
||||
}
|
||||
else
|
||||
{
|
||||
RgbConverter.Convert<TColor, TColorArg, ColorRGB<byte>, byte>(Bits, Width, Height, out data);
|
||||
RgbConverter.Convert<TColor, TColorArg, Color<byte, R, G, B>, byte>(Bits, Width, Height, out data);
|
||||
components = ColorComponents.RedGreenBlue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,18 +63,32 @@ internal static partial class Program
|
||||
|
||||
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);
|
||||
}
|
||||
WriteAllBytes(Path.Combine(dirPath, name + ".bgra"), bitmap.Bits);
|
||||
break;
|
||||
case "bmp":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsBmp(Path.Combine(dirPath, name + ".bmp"));
|
||||
break;
|
||||
case "exr":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsExr(Path.Combine(dirPath, name + ".exr"));
|
||||
break;
|
||||
case "hdr":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsHdr(Path.Combine(dirPath, name + ".hdr"));
|
||||
break;
|
||||
case "jpg":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsJpg(Path.Combine(dirPath, name + ".jpg"));
|
||||
break;
|
||||
case "png":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsPng(Path.Combine(dirPath, name + ".png"));
|
||||
break;
|
||||
case "tga":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsTga(Path.Combine(dirPath, name + ".tga"));
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException(outputType);
|
||||
@@ -239,4 +253,4 @@ internal static partial class Program
|
||||
using Microsoft.Win32.SafeHandles.SafeFileHandle sfh = File.OpenHandle(path, FileMode.Create, FileAccess.Write, FileShare.Read);
|
||||
RandomAccess.Write(sfh, bytes, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.ConsoleApp;
|
||||
static partial class Program
|
||||
@@ -9,82 +10,97 @@ static partial class Program
|
||||
private static void DecodeRgb(ReadOnlySpan<byte> input, int width, int height, string modeString, Span<byte> output)
|
||||
{
|
||||
Console.WriteLine("Arg at index 5 : mode");
|
||||
Console.WriteLine(" 0 - R8SByte");
|
||||
Console.WriteLine(" 1 - RG8SByte");
|
||||
Console.WriteLine(" 2 - RGB8SByte");
|
||||
Console.WriteLine(" 3 - RGBA8SByte");
|
||||
Console.WriteLine(" 4 - A8SByte");
|
||||
Console.WriteLine(" 5 - R8Byte");
|
||||
Console.WriteLine(" 6 - RG8Byte");
|
||||
Console.WriteLine(" 7 - RGB8Byte");
|
||||
Console.WriteLine(" 8 - RGBA8Byte");
|
||||
Console.WriteLine(" 9 - A8Byte");
|
||||
Console.WriteLine(" 10 - R16Int16");
|
||||
Console.WriteLine(" 11 - RG16Int16");
|
||||
Console.WriteLine(" 12 - RGB16Int16");
|
||||
Console.WriteLine(" 13 - RGBA16Int16");
|
||||
Console.WriteLine(" 14 - A16Int16");
|
||||
Console.WriteLine(" 15 - R16UInt16");
|
||||
Console.WriteLine(" 16 - RG16UInt16");
|
||||
Console.WriteLine(" 17 - RGB16UInt16");
|
||||
Console.WriteLine(" 18 - RGBA16UInt16");
|
||||
Console.WriteLine(" 19 - A16UInt16");
|
||||
Console.WriteLine(" 20 - R32Int32");
|
||||
Console.WriteLine(" 21 - RG32Int32");
|
||||
Console.WriteLine(" 22 - RGB32Int32");
|
||||
Console.WriteLine(" 23 - RGBA32Int32");
|
||||
Console.WriteLine(" 24 - A32Int32");
|
||||
Console.WriteLine(" 25 - R32UInt32");
|
||||
Console.WriteLine(" 26 - RG32UInt32");
|
||||
Console.WriteLine(" 27 - RGB32UInt32");
|
||||
Console.WriteLine(" 28 - RGBA32UInt32");
|
||||
Console.WriteLine(" 29 - A32UInt32");
|
||||
Console.WriteLine(" 30 - R64Int64");
|
||||
Console.WriteLine(" 31 - RG64Int64");
|
||||
Console.WriteLine(" 32 - RGB64Int64");
|
||||
Console.WriteLine(" 33 - RGBA64Int64");
|
||||
Console.WriteLine(" 34 - A64Int64");
|
||||
Console.WriteLine(" 35 - R64UInt64");
|
||||
Console.WriteLine(" 36 - RG64UInt64");
|
||||
Console.WriteLine(" 37 - RGB64UInt64");
|
||||
Console.WriteLine(" 38 - RGBA64UInt64");
|
||||
Console.WriteLine(" 39 - A64UInt64");
|
||||
Console.WriteLine(" 40 - R128Int128");
|
||||
Console.WriteLine(" 41 - RG128Int128");
|
||||
Console.WriteLine(" 42 - RGB128Int128");
|
||||
Console.WriteLine(" 43 - RGBA128Int128");
|
||||
Console.WriteLine(" 44 - A128Int128");
|
||||
Console.WriteLine(" 45 - R128UInt128");
|
||||
Console.WriteLine(" 46 - RG128UInt128");
|
||||
Console.WriteLine(" 47 - RGB128UInt128");
|
||||
Console.WriteLine(" 48 - RGBA128UInt128");
|
||||
Console.WriteLine(" 49 - A128UInt128");
|
||||
Console.WriteLine(" 50 - R16Half");
|
||||
Console.WriteLine(" 51 - RG16Half");
|
||||
Console.WriteLine(" 52 - RGB16Half");
|
||||
Console.WriteLine(" 53 - RGBA16Half");
|
||||
Console.WriteLine(" 54 - A16Half");
|
||||
Console.WriteLine(" 55 - R32Single");
|
||||
Console.WriteLine(" 56 - RG32Single");
|
||||
Console.WriteLine(" 57 - RGB32Single");
|
||||
Console.WriteLine(" 58 - RGBA32Single");
|
||||
Console.WriteLine(" 59 - A32Single");
|
||||
Console.WriteLine(" 60 - R64Double");
|
||||
Console.WriteLine(" 61 - RG64Double");
|
||||
Console.WriteLine(" 62 - RGB64Double");
|
||||
Console.WriteLine(" 63 - RGBA64Double");
|
||||
Console.WriteLine(" 64 - A64Double");
|
||||
Console.WriteLine(" 65 - R128Decimal");
|
||||
Console.WriteLine(" 66 - RG128Decimal");
|
||||
Console.WriteLine(" 67 - RGB128Decimal");
|
||||
Console.WriteLine(" 68 - RGBA128Decimal");
|
||||
Console.WriteLine(" 69 - A128Decimal");
|
||||
Console.WriteLine(" 70 - ARGB16");
|
||||
Console.WriteLine(" 71 - ARGB32");
|
||||
Console.WriteLine(" 72 - BGRA32");
|
||||
Console.WriteLine(" 73 - RGB16");
|
||||
Console.WriteLine(" 74 - RGB9e5");
|
||||
Console.WriteLine(" 75 - RGBA16");
|
||||
Console.WriteLine(" 0 - R_SByte");
|
||||
Console.WriteLine(" 1 - RG_SByte");
|
||||
Console.WriteLine(" 2 - RGB_SByte");
|
||||
Console.WriteLine(" 3 - RGBA_SByte");
|
||||
Console.WriteLine(" 4 - A_SByte");
|
||||
Console.WriteLine(" 5 - 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");
|
||||
int mode = int.Parse(modeString);
|
||||
switch(mode)
|
||||
{
|
||||
@@ -179,141 +195,186 @@ static partial class Program
|
||||
RgbConverter.Convert<ColorA<uint>, uint, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 30:
|
||||
RgbConverter.Convert<ColorR<long>, long, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<nint>, nint, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 31:
|
||||
RgbConverter.Convert<ColorRG<long>, long, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<nint>, nint, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 32:
|
||||
RgbConverter.Convert<ColorRGB<long>, long, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<nint>, nint, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 33:
|
||||
RgbConverter.Convert<ColorRGBA<long>, long, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<nint>, nint, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 34:
|
||||
RgbConverter.Convert<ColorA<long>, long, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<nint>, nint, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 35:
|
||||
RgbConverter.Convert<ColorR<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<nuint>, nuint, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 36:
|
||||
RgbConverter.Convert<ColorRG<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<nuint>, nuint, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 37:
|
||||
RgbConverter.Convert<ColorRGB<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<nuint>, nuint, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 38:
|
||||
RgbConverter.Convert<ColorRGBA<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<nuint>, nuint, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 39:
|
||||
RgbConverter.Convert<ColorA<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<nuint>, nuint, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 40:
|
||||
RgbConverter.Convert<ColorR<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<long>, long, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 41:
|
||||
RgbConverter.Convert<ColorRG<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<long>, long, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 42:
|
||||
RgbConverter.Convert<ColorRGB<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<long>, long, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 43:
|
||||
RgbConverter.Convert<ColorRGBA<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<long>, long, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 44:
|
||||
RgbConverter.Convert<ColorA<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<long>, long, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 45:
|
||||
RgbConverter.Convert<ColorR<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 46:
|
||||
RgbConverter.Convert<ColorRG<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 47:
|
||||
RgbConverter.Convert<ColorRGB<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 48:
|
||||
RgbConverter.Convert<ColorRGBA<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 49:
|
||||
RgbConverter.Convert<ColorA<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<ulong>, ulong, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 50:
|
||||
RgbConverter.Convert<ColorR<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 51:
|
||||
RgbConverter.Convert<ColorRG<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 52:
|
||||
RgbConverter.Convert<ColorRGB<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 53:
|
||||
RgbConverter.Convert<ColorRGBA<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 54:
|
||||
RgbConverter.Convert<ColorA<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<Int128>, Int128, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 55:
|
||||
RgbConverter.Convert<ColorR<float>, float, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 56:
|
||||
RgbConverter.Convert<ColorRG<float>, float, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 57:
|
||||
RgbConverter.Convert<ColorRGB<float>, float, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 58:
|
||||
RgbConverter.Convert<ColorRGBA<float>, float, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 59:
|
||||
RgbConverter.Convert<ColorA<float>, float, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<UInt128>, UInt128, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 60:
|
||||
RgbConverter.Convert<ColorR<double>, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 61:
|
||||
RgbConverter.Convert<ColorRG<double>, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 62:
|
||||
RgbConverter.Convert<ColorRGB<double>, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 63:
|
||||
RgbConverter.Convert<ColorRGBA<double>, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 64:
|
||||
RgbConverter.Convert<ColorA<double>, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<Half>, Half, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 65:
|
||||
RgbConverter.Convert<ColorR<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<float>, float, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 66:
|
||||
RgbConverter.Convert<ColorRG<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<float>, float, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 67:
|
||||
RgbConverter.Convert<ColorRGB<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<float>, float, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 68:
|
||||
RgbConverter.Convert<ColorRGBA<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<float>, float, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 69:
|
||||
RgbConverter.Convert<ColorA<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<float>, float, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 70:
|
||||
RgbConverter.Convert<ColorARGB16, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorR<NFloat>, NFloat, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 71:
|
||||
RgbConverter.Convert<ColorARGB32, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRG<NFloat>, NFloat, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 72:
|
||||
RgbConverter.Convert<ColorBGRA32, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGB<NFloat>, NFloat, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 73:
|
||||
RgbConverter.Convert<ColorRGB16, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorRGBA<NFloat>, NFloat, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 74:
|
||||
RgbConverter.Convert<ColorRGB9e5, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
RgbConverter.Convert<ColorA<NFloat>, NFloat, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 75:
|
||||
RgbConverter.Convert<ColorR<double>, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 76:
|
||||
RgbConverter.Convert<ColorRG<double>, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 77:
|
||||
RgbConverter.Convert<ColorRGB<double>, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 78:
|
||||
RgbConverter.Convert<ColorRGBA<double>, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 79:
|
||||
RgbConverter.Convert<ColorA<double>, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 80:
|
||||
RgbConverter.Convert<ColorR<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 81:
|
||||
RgbConverter.Convert<ColorRG<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 82:
|
||||
RgbConverter.Convert<ColorRGB<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 83:
|
||||
RgbConverter.Convert<ColorRGBA<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 84:
|
||||
RgbConverter.Convert<ColorA<decimal>, decimal, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 85:
|
||||
RgbConverter.Convert<ColorARGB16, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 86:
|
||||
RgbConverter.Convert<ColorARGB32, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 87:
|
||||
RgbConverter.Convert<ColorBGRA32, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 88:
|
||||
RgbConverter.Convert<ColorRGB16, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 89:
|
||||
RgbConverter.Convert<ColorRGB9e5, double, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
case 90:
|
||||
RgbConverter.Convert<ColorRGBA16, byte, ColorBGRA32, byte>(input, width, height, output);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -1,9 +1,2 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
-6
@@ -1,9 +1,3 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.SourceGeneration.Common;
|
||||
|
||||
@@ -13,12 +14,15 @@ public static class CSharpPrimitives
|
||||
{ typeof(ushort), new Data<ushort>() },
|
||||
{ typeof(int), new Data<int>() },
|
||||
{ typeof(uint), new Data<uint>() },
|
||||
{ typeof(nint), new Data<nint>() },
|
||||
{ typeof(nuint), new Data<nuint>() },
|
||||
{ typeof(long), new Data<long>() },
|
||||
{ typeof(ulong), new Data<ulong>() },
|
||||
{ typeof(Int128), new Data<Int128>() },
|
||||
{ typeof(UInt128), new Data<UInt128>() },
|
||||
{ typeof(Half), new Data<Half>() },
|
||||
{ typeof(float), new Data<float>() },
|
||||
{ typeof(NFloat), new Data<NFloat>() },
|
||||
{ typeof(double), new Data<double>() },
|
||||
{ typeof(decimal), new Data<decimal>() },
|
||||
};
|
||||
@@ -31,14 +35,42 @@ public static class CSharpPrimitives
|
||||
|
||||
public static Data FirstData => List[0];
|
||||
|
||||
public static bool HasPointerSize(Type type, [NotNullWhen(true)] out Type? bit32Type, [NotNullWhen(true)] out Type? bit64Type)
|
||||
{
|
||||
if (type == typeof(nint))
|
||||
{
|
||||
bit32Type = typeof(int);
|
||||
bit64Type = typeof(long);
|
||||
return true;
|
||||
}
|
||||
else if (type == typeof(nuint))
|
||||
{
|
||||
bit32Type = typeof(uint);
|
||||
bit64Type = typeof(ulong);
|
||||
return true;
|
||||
}
|
||||
else if (type == typeof(NFloat))
|
||||
{
|
||||
bit32Type = typeof(float);
|
||||
bit64Type = typeof(double);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bit32Type = null;
|
||||
bit64Type = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsFloatingPoint(Type type)
|
||||
{
|
||||
return type == typeof(Half) || type == typeof(float) || type == typeof(double) || type == typeof(decimal);
|
||||
return type == typeof(Half) || type == typeof(float) || type == typeof(NFloat) || type == typeof(double) || type == typeof(decimal);
|
||||
}
|
||||
|
||||
public static bool IsUnsignedInteger(Type type)
|
||||
{
|
||||
return type == typeof(byte) || type == typeof(ushort) || type == typeof(uint) || type == typeof(ulong) || type == typeof(UInt128);
|
||||
return type == typeof(byte) || type == typeof(ushort) || type == typeof(uint) || type == typeof(nuint) || type == typeof(ulong) || type == typeof(UInt128);
|
||||
}
|
||||
|
||||
public static bool IsSignedInteger(Type type, [NotNullWhen(true)] out Type? unsignedType)
|
||||
@@ -55,6 +87,10 @@ public static class CSharpPrimitives
|
||||
{
|
||||
unsignedType = typeof(uint);
|
||||
}
|
||||
else if (type == typeof(nint))
|
||||
{
|
||||
unsignedType = typeof(nuint);
|
||||
}
|
||||
else if (type == typeof(long))
|
||||
{
|
||||
unsignedType = typeof(ulong);
|
||||
@@ -113,6 +149,14 @@ public static class CSharpPrimitives
|
||||
{
|
||||
return "uint";
|
||||
}
|
||||
else if (type == typeof(nint))
|
||||
{
|
||||
return "nint";
|
||||
}
|
||||
else if (type == typeof(nuint))
|
||||
{
|
||||
return "nuint";
|
||||
}
|
||||
else if (type == typeof(long))
|
||||
{
|
||||
return "long";
|
||||
@@ -157,6 +201,21 @@ public static class CSharpPrimitives
|
||||
public string TypeName => Type.Name;
|
||||
public string LangName => GetLangName(Type);
|
||||
public bool CanBeConstant => CanBeConstant(Type);
|
||||
public bool HasPointerSize([NotNullWhen(true)] out Data? bit32Data, [NotNullWhen(true)] out Data? bit64Data)
|
||||
{
|
||||
if (CSharpPrimitives.HasPointerSize(Type, out Type? bit32Type, out Type? bit64Type))
|
||||
{
|
||||
bit32Data = Dictionary[bit32Type];
|
||||
bit64Data = Dictionary[bit64Type];
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bit32Data = null;
|
||||
bit64Data = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public bool IsFloatingPoint => IsFloatingPoint(Type);
|
||||
public bool IsUnsignedInteger => IsUnsignedInteger(Type);
|
||||
public bool IsSignedInteger([NotNullWhen(true)] out Data? unsignedData)
|
||||
@@ -182,6 +241,19 @@ public static class CSharpPrimitives
|
||||
{
|
||||
public override Type Type => typeof(T);
|
||||
|
||||
public override int Size => Unsafe.SizeOf<T>();
|
||||
public override int Size
|
||||
{
|
||||
get
|
||||
{
|
||||
if (CSharpPrimitives.HasPointerSize(Type, out _, out _))
|
||||
{
|
||||
return 6;//arbitrary value between 4 and 8
|
||||
}
|
||||
else
|
||||
{
|
||||
return Unsafe.SizeOf<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-3
@@ -2,9 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace AssetRipper.TextureDecoder.TestGenerator
|
||||
|
||||
public string ChannelTypeName => CSharpPrimitives.Dictionary[ChannelType].LangName;
|
||||
|
||||
public static GenerationData Create<T>(int colorSize) where T : IColorBase
|
||||
public static GenerationData Create<T>(int colorSize) where T : IColor
|
||||
{
|
||||
return new GenerationData()
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@ using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using AssetRipper.TextureDecoder.SourceGeneration.Common;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.TestGenerator
|
||||
{
|
||||
@@ -35,6 +36,7 @@ namespace AssetRipper.TextureDecoder.TestGenerator
|
||||
writer.WriteLine();
|
||||
writer.WriteLine($"using {typeof(RgbConverter).Namespace};");
|
||||
writer.WriteLine($"using {typeof(ColorBGRA32).Namespace};");
|
||||
writer.WriteLine($"using {typeof(NFloat).Namespace};");
|
||||
writer.WriteLine();
|
||||
|
||||
writer.WriteFileScopedNamespace("AssetRipper.TextureDecoder.ConsoleApp");
|
||||
@@ -60,7 +62,7 @@ namespace AssetRipper.TextureDecoder.TestGenerator
|
||||
writer.WriteLine($"case {i}:");
|
||||
using (new Indented(writer))
|
||||
{
|
||||
writer.WriteLine($"RgbConverter.Convert<{colorName}, {channelName}, ColorBGRA32, byte>(input, width, height, output);");
|
||||
writer.WriteLine($"RgbConverter.Convert<{colorName}, {channelName}, Color<byte, B, G, R, A>, byte>(input, width, height, output);");
|
||||
writer.WriteLine("break;");
|
||||
}
|
||||
}
|
||||
@@ -79,10 +81,9 @@ namespace AssetRipper.TextureDecoder.TestGenerator
|
||||
{
|
||||
foreach (CSharpPrimitives.Data primitiveData in CSharpPrimitives.List)
|
||||
{
|
||||
int primitiveBitSize = primitiveData.Size * 8;
|
||||
foreach (string genericColorName in TestClassGenerator.GetGenericColorNames())
|
||||
{
|
||||
string prettyName = $"{genericColorName}{primitiveBitSize}{primitiveData.TypeName}";
|
||||
string prettyName = $"{genericColorName}_{primitiveData.TypeName}";
|
||||
yield return (prettyName, $"{genericColorName}<{primitiveData.LangName}>", primitiveData.LangName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ internal static class TestClassGenerator
|
||||
private const string GenericOutputNamespace = OutputNamespace + ".Generic";
|
||||
private const string GenericOutputFolder = OutputFolder + "Generic/";
|
||||
|
||||
private static readonly char[] channels = new char[] { 'R', 'G', 'B', 'A' };
|
||||
private static readonly char[] channels = ['R', 'G', 'B', 'A'];
|
||||
|
||||
internal static void GenerateTestClasses()
|
||||
{
|
||||
@@ -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))
|
||||
{
|
||||
}
|
||||
@@ -354,4 +354,4 @@ internal static class TestClassGenerator
|
||||
textWriter.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageReference Include="NUnit" Version="4.1.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
<PackageReference Include="NUnit.Analyzers" Version="3.8.0">
|
||||
<PackageReference Include="NUnit.Analyzers" Version="4.1.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -74,4 +74,10 @@ public sealed class AstcTests
|
||||
}
|
||||
ByteArrayDeviation.AssertMinimalDeviation(decompressedData, AndroidTextures.Logo_00.Data, maxMeanDeviation, maxStandardDeviation);
|
||||
}
|
||||
|
||||
[TestCase(new byte[16] { 83, 1, 147, 104, 198, 173, 55, 116, 182, 66, 105, 11, 102, 43, 148, 125 }, 8, 8, TestName = "GetBits Should not throw in DecodeIntseq")]
|
||||
public void DecodesBlockWithoutThrowing(byte[] block, int blockWidth, int blockHeight)
|
||||
{
|
||||
Assert.DoesNotThrow(() => AstcDecoder.DecodeASTC(block, blockWidth, blockHeight, blockWidth, blockHeight, out _));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using AssetRipper.TextureDecoder.Bc;
|
||||
using AssetRipper.TextureDecoder.Dxt;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
{
|
||||
@@ -79,40 +81,33 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressBC6HFastTest()
|
||||
[TestCase("test.bc6h_fast")]
|
||||
[TestCase("test.bc6h_normal")]
|
||||
[TestCase("test.bc6h_best")]
|
||||
public void DecompressBC6HTest(string fileName)
|
||||
{
|
||||
AssertCorrectBC6HDecompression(TestFileFolders.BcTestFiles + "test.bc6h_fast", 512, 512, false);
|
||||
AssertCorrectBC6HDecompression(TestFileFolders.BcTestFiles + fileName, 512, 512, false);
|
||||
}
|
||||
|
||||
[TestCase("test.bc7_fast")]
|
||||
[TestCase("test.bc7_normal")]
|
||||
[TestCase("test.bc7_best")]
|
||||
public void DecompressBC7Test(string fileName)
|
||||
{
|
||||
AssertCorrectBC7Decompression(TestFileFolders.BcTestFiles + fileName, 512, 512);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressBC6HNormalTest()
|
||||
[Ignore("Not implemented")]
|
||||
public void BC7PartialBlockTest([Range(1, 4)] int width, [Range(1, 4)] int height)
|
||||
{
|
||||
AssertCorrectBC6HDecompression(TestFileFolders.BcTestFiles + "test.bc6h_normal", 512, 512, false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressBC6HBestTest()
|
||||
{
|
||||
AssertCorrectBC6HDecompression(TestFileFolders.BcTestFiles + "test.bc6h_best", 512, 512, false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressBC7FastTest()
|
||||
{
|
||||
AssertCorrectBC7Decompression(TestFileFolders.BcTestFiles + "test.bc7_fast", 512, 512);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressBC7NormalTest()
|
||||
{
|
||||
AssertCorrectBC7Decompression(TestFileFolders.BcTestFiles + "test.bc7_normal", 512, 512);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressBC7BestTest()
|
||||
{
|
||||
AssertCorrectBC7Decompression(TestFileFolders.BcTestFiles + "test.bc7_best", 512, 512);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bc7_best").AsSpan()[..Bc7.BlockSize];
|
||||
int bytesRead = Bc7.Decompress(data, width, height, out byte[] decodedData);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA32>()));
|
||||
});
|
||||
}
|
||||
|
||||
private static void AssertCorrectBC6HDecompression(string path, int width, int height, bool isSigned)
|
||||
|
||||
@@ -1,109 +1,102 @@
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
using AssetRipper.TextureDecoder.Etc;
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests;
|
||||
|
||||
public sealed class EtcTests
|
||||
{
|
||||
public sealed class EtcTests
|
||||
private static DecodingDelegate ETCDelegate { get; } = (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);
|
||||
|
||||
[Test]
|
||||
public void DecompressETCTest() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.etc", ETCDelegate);
|
||||
|
||||
[Test]
|
||||
public void DecompressETC2Test() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.etc2", ETC2Delegate);
|
||||
|
||||
[Test]
|
||||
public void DecompressETC2A1Test() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.etc2a1", ETC2A1Delegate);
|
||||
|
||||
[Test]
|
||||
public void DecompressETC2A8Test() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.etc2a8", ETC2A8Delegate);
|
||||
|
||||
[Test]
|
||||
public void DecompressEACRSignedTest() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.eac_r", EACRSignedDelegate);
|
||||
|
||||
[Test]
|
||||
public void DecompressEACRUnsignedTest() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.eac_r", EACRUnsignedDelegate);
|
||||
|
||||
[Test]
|
||||
public void DecompressEACRGSignedTest() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.eac_rg", EACRGSignedDelegate);
|
||||
|
||||
[Test]
|
||||
public void DecompressEACRGUnsignedTest() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.eac_rg", EACRGUnsignedDelegate);
|
||||
|
||||
[Test]
|
||||
public void CorrectnessETCTest() => AssertCorrectDecompression<AndroidTextures.Logo_12, ColorRGB<byte>>(ETCDelegate, .05, 2.1);
|
||||
|
||||
[Test]
|
||||
public void CorrectnessETC2Test() => AssertCorrectDecompression<AndroidTextures.Logo_11, ColorRGB<byte>>(ETC2Delegate, .15, 1.6);
|
||||
|
||||
[Test]
|
||||
public void CorrectnessETC2A1Test() => AssertCorrectDecompression<AndroidTextures.Logo_08>(ETC2A1Delegate, .15, 1.7);
|
||||
|
||||
[Test]
|
||||
public void CorrectnessETC2A8Test() => AssertCorrectDecompression<AndroidTextures.Logo_07>(ETC2A8Delegate, .15, 1.6);
|
||||
|
||||
[Test]
|
||||
public void CorrectnessEACRUnsignedTest() => AssertCorrectDecompression<AndroidTextures.Logo_16, ColorR<byte>>(EACRUnsignedDelegate, .05, .5);
|
||||
|
||||
[Test]
|
||||
public void CorrectnessEACRGUnsignedTest() => AssertCorrectDecompression<AndroidTextures.Logo_15, ColorRG<byte>>(EACRGUnsignedDelegate, .1, .6);
|
||||
|
||||
private delegate int DecodingDelegate(ArraySegment<byte> data, int width, int height, out byte[] decompressedData);
|
||||
|
||||
private static void AssertCorrectByteCountReadFor256SquareWithMips(string path, DecodingDelegate decoder)
|
||||
{
|
||||
[Test]
|
||||
public void DecompressETCTest()
|
||||
byte[] data = File.ReadAllBytes(path);
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.EtcTestFiles + "test.etc");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Etc.EtcDecoder.DecompressETC(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
int bytesRead = decoder.Invoke(new ArraySegment<byte>(data, totalBytesRead, data.Length - totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
private static void AssertCorrectDecompression<T>(DecodingDelegate decoder, double maxMeanDeviation, double maxStandardDeviation) where T : ITexture
|
||||
{
|
||||
AssertCorrectDecompression<T, ColorRGBA<byte>>(decoder, maxMeanDeviation, maxStandardDeviation);
|
||||
}
|
||||
|
||||
private static void AssertCorrectDecompression<TTexture, TColor>(DecodingDelegate decoder, double maxMeanDeviation, double maxStandardDeviation) where TTexture : ITexture where TColor : unmanaged, IColor<TColor, byte>
|
||||
{
|
||||
byte[] data = TTexture.Data;
|
||||
int bytesRead = decoder.Invoke(new ArraySegment<byte>(data), TTexture.Width, TTexture.Height, out byte[] decompressedData);
|
||||
if (!TTexture.Mips)
|
||||
{
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressETC2Test()
|
||||
byte[] comparisonData = AndroidTextures.Logo_00.Data;
|
||||
|
||||
//Remove unused color channels
|
||||
if (typeof(TColor) != typeof(ColorBGRA32) && typeof(TColor) != typeof(ColorRGBA<byte>))
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.EtcTestFiles + "test.etc2");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
Span<ColorBGRA32> pixels = MemoryMarshal.Cast<byte, ColorBGRA32>((Span<byte>)comparisonData);
|
||||
for (int i = 0; i < pixels.Length; i++)
|
||||
{
|
||||
int bytesRead = Etc.EtcDecoder.DecompressETC2(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
pixels[i] = pixels[i].Convert<ColorBGRA32, byte, TColor, byte>().Convert<TColor, byte, ColorBGRA32, byte>();
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressETC2A1Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.EtcTestFiles + "test.etc2a1");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Etc.EtcDecoder.DecompressETC2A1(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressETC2A8Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.EtcTestFiles + "test.etc2a8");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Etc.EtcDecoder.DecompressETC2A8(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressEACRSignedTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.EtcTestFiles + "test.eac_r");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Etc.EtcDecoder.DecompressEACRSigned(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressEACRUnsignedTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.EtcTestFiles + "test.eac_r");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Etc.EtcDecoder.DecompressEACRUnsigned(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressEACRGSignedTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.EtcTestFiles + "test.eac_rg");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Etc.EtcDecoder.DecompressEACRGSigned(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressEACRGUnsignedTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.EtcTestFiles + "test.eac_rg");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Etc.EtcDecoder.DecompressEACRGUnsigned(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
ByteArrayDeviation.AssertMinimalDeviation(decompressedData, comparisonData, maxMeanDeviation, maxStandardDeviation);
|
||||
}
|
||||
}
|
||||
@@ -194,6 +194,18 @@ public partial class ColorARGB16Tests
|
||||
LosslessConversion.Assert<ColorARGB16, byte, ColorRGBA<uint>, uint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_nint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB16, byte, ColorRGBA<nint>, nint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_nuint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorARGB16, byte, ColorRGBA<nuint>, nuint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_long()
|
||||
{
|
||||
|
||||
@@ -194,6 +194,18 @@ public partial class ColorARGB32Tests
|
||||
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()
|
||||
{
|
||||
|
||||
@@ -194,6 +194,18 @@ public partial class ColorBGRA32Tests
|
||||
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()
|
||||
{
|
||||
|
||||
@@ -230,6 +230,30 @@ public partial class ColorRGB16Tests
|
||||
LosslessConversion.Assert<ColorRGB16, byte, ColorRGB<uint>, uint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_nint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorRGB16, byte, ColorRGBA<nint>, nint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGB_nint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorRGB16, byte, ColorRGB<nint>, nint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_nuint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorRGB16, byte, ColorRGBA<nuint>, nuint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGB_nuint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorRGB16, byte, ColorRGB<nuint>, nuint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_long()
|
||||
{
|
||||
|
||||
@@ -194,6 +194,18 @@ public partial class ColorRGBA16Tests
|
||||
LosslessConversion.Assert<ColorRGBA16, byte, ColorRGBA<uint>, uint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_nint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorRGBA16, byte, ColorRGBA<nint>, nint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_nuint()
|
||||
{
|
||||
LosslessConversion.Assert<ColorRGBA16, byte, ColorRGBA<nuint>, nuint>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionIsLosslessToColorRGBA_long()
|
||||
{
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
internal static class ColorRandom<TColor, TChannel> where TColor : unmanaged, IColor<TChannel> where TChannel : unmanaged
|
||||
internal static class ColorRandom<TColor, TChannelValue> where TColor : unmanaged, IColor<TChannelValue> where TChannelValue : unmanaged
|
||||
{
|
||||
public static TChannel MakeRandomValue()
|
||||
public static TChannelValue MakeRandomValue()
|
||||
{
|
||||
ulong value = TestContext.CurrentContext.Random.NextULong();
|
||||
return NumericConversion.Convert<ulong, TChannel>(value);
|
||||
return NumericConversion.Convert<ulong, TChannelValue>(value);
|
||||
}
|
||||
|
||||
public static TColor MakeRandomColor()
|
||||
|
||||
@@ -3,19 +3,19 @@ using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
|
||||
internal partial class GenericColorTests<TColor, TChannel> where TColor : unmanaged, IColor<TChannel> where TChannel : unmanaged
|
||||
internal partial class GenericColorTests<TColor, TChannelValue> where TColor : unmanaged, IColor<TChannelValue> where TChannelValue : unmanaged
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<TColor>(), Is.EqualTo(Color.GetChannelCount<TColor>() * Unsafe.SizeOf<TChannel>()));
|
||||
Assert.That(Unsafe.SizeOf<TColor>(), Is.EqualTo(Color.GetChannelCount<TColor>() * Unsafe.SizeOf<TChannelValue>()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
TChannel r = color.R;
|
||||
TChannelValue r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
@@ -24,7 +24,7 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
TChannel g = color.G;
|
||||
TChannelValue g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
@@ -33,7 +33,7 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
TChannel b = color.B;
|
||||
TChannelValue b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
@@ -42,7 +42,7 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
TChannel a = color.A;
|
||||
TChannelValue a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
@@ -51,9 +51,9 @@ 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(() =>
|
||||
{
|
||||
@@ -67,9 +67,9 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
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(() =>
|
||||
{
|
||||
@@ -83,9 +83,9 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
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(() =>
|
||||
{
|
||||
@@ -99,9 +99,9 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
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(() =>
|
||||
{
|
||||
@@ -115,7 +115,7 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
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);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
@@ -129,7 +129,7 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
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(() =>
|
||||
{
|
||||
@@ -140,7 +140,7 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -33,6 +33,16 @@ namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
[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(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(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(ColorR<long>), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<long>), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<long>), typeof(long) })]
|
||||
@@ -63,6 +73,11 @@ namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
[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(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(ColorR<double>), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRG<double>), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<double>), typeof(double) })]
|
||||
@@ -73,6 +88,6 @@ namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
[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>
|
||||
partial class GenericColorTests<TColor, TChannelValue>
|
||||
{
|
||||
}
|
||||
@@ -9,6 +9,8 @@ namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(Int128) })]
|
||||
@@ -19,6 +21,8 @@ namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(byte), typeof(Int128) })]
|
||||
@@ -27,6 +31,8 @@ namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(Int128) })]
|
||||
@@ -35,22 +41,40 @@ namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ushort), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(int) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(uint), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nint), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nint), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nint), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nint), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nint), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nint), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nuint), typeof(nint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nuint), typeof(nuint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nuint), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nuint), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nuint), typeof(Int128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(nuint), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(long), typeof(long) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(long), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(long), typeof(Int128) })]
|
||||
@@ -65,9 +89,13 @@ namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(UInt128), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(Half), typeof(Half) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(Half), typeof(float) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(Half), typeof(NFloat) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(Half), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(float), typeof(float) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(float), typeof(NFloat) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(float), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(NFloat), typeof(NFloat) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(NFloat), typeof(double) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(double), typeof(double) })]
|
||||
partial class LosslessColorTests<T1, T2>
|
||||
{
|
||||
|
||||
@@ -3,11 +3,11 @@ using System.Numerics;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(sbyte), typeof(byte) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(short), typeof(ushort) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(int), typeof(uint) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(long), typeof(ulong) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(Int128), typeof(UInt128) })]
|
||||
[TestFixture(TypeArgs = [typeof(sbyte), typeof(byte)])]
|
||||
[TestFixture(TypeArgs = [typeof(short), typeof(ushort)])]
|
||||
[TestFixture(TypeArgs = [typeof(int), typeof(uint)])]
|
||||
[TestFixture(TypeArgs = [typeof(long), typeof(ulong)])]
|
||||
[TestFixture(TypeArgs = [typeof(Int128), typeof(UInt128)])]
|
||||
internal class NumericConversionExtremesTests<TSigned, TUnsigned>
|
||||
where TSigned : unmanaged, IMinMaxValue<TSigned>
|
||||
where TUnsigned : unmanaged, IMinMaxValue<TUnsigned>
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,42 @@
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
using AssetRipper.TextureDecoder.Pvrtc;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests;
|
||||
|
||||
public sealed class PvrtcTests
|
||||
{
|
||||
public sealed class PvrtcTests
|
||||
[Test]
|
||||
public void DecompressPVRTCTest()
|
||||
{
|
||||
[Test]
|
||||
public void DecompressPVRTCTest()
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.PvrtcTestFiles + "test.pvrtc4");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.PvrtcTestFiles + "test.pvrtc4");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Pvrtc.PvrtcDecoder.DecompressPVRTC(data, size, size, false, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
int bytesRead = PvrtcDecoder.DecompressPVRTC(data, size, size, false, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
private const double MaxMeanDeviation4 = 0.05;
|
||||
private const double MaxStandardDeviation4 = 1.4;
|
||||
|
||||
private const double MaxMeanDeviation2 = 0.10;
|
||||
private const double MaxStandardDeviation2 = 2.6;
|
||||
|
||||
[Test]
|
||||
public void Decompress_4() => AssertCorrectDecompression<AndroidTextures.Logo_13>(false, MaxMeanDeviation4, MaxStandardDeviation4);
|
||||
|
||||
[Test]
|
||||
public void Decompress_2() => AssertCorrectDecompression<AndroidTextures.Logo_14>(true, MaxMeanDeviation2, MaxStandardDeviation2);
|
||||
|
||||
private static void AssertCorrectDecompression<T>(bool do2bitMode, double maxMeanDeviation, double maxStandardDeviation) where T : ITexture
|
||||
{
|
||||
ReadOnlySpan<byte> data = T.Data;
|
||||
int bytesRead = PvrtcDecoder.DecompressPVRTC(data, T.Width, T.Height, do2bitMode, out byte[] decompressedData);
|
||||
if (!T.Mips)
|
||||
{
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
ByteArrayDeviation.AssertMinimalDeviation(decompressedData, AndroidTextures.Logo_00.Data, maxMeanDeviation, maxStandardDeviation);
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
global using AssetRipper.TextureDecoder.Paths;
|
||||
global using NUnit.Framework;
|
||||
global using System.Runtime.InteropServices;
|
||||
@@ -1,5 +1,6 @@
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using AssetRipper.TextureDecoder.Yuy2;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
{
|
||||
@@ -12,10 +13,12 @@ namespace AssetRipper.TextureDecoder.Tests
|
||||
public void DecompressYUY2Test()
|
||||
{
|
||||
byte[] input = File.ReadAllBytes(TestFileFolders.Yuy2TestFiles + "test.yuy2");
|
||||
int bytesRead = Yuy2Decoder.DecompressYUY2(input, TestImageWidth, TestImageHeight, out byte[] originalOutput);
|
||||
Assert.That(bytesRead, Is.EqualTo(input.Length));
|
||||
Yuy2Decoder.DecompressYUY2<ColorBGRA32, byte>(input, TestImageWidth, TestImageHeight, out byte[] genericOutput);
|
||||
Assert.That(originalOutput, Is.EqualTo(genericOutput));
|
||||
int bytesRead = Yuy2Decoder.DecompressYUY2<ColorBGRA32, byte>(input, TestImageWidth, TestImageHeight, out byte[] decodedData);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(bytesRead, Is.EqualTo(input.Length));
|
||||
Assert.That(decodedData, Has.Length.EqualTo(TestImageWidth * TestImageHeight * Unsafe.SizeOf<ColorBGRA32>()));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<LangVersion>Latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<IsTrimmable>true</IsTrimmable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<RootNamespace>AssetRipper.TextureDecoder</RootNamespace>
|
||||
<Authors>ds5678</Authors>
|
||||
<Company>AssetRipper</Company>
|
||||
<Version>2.0.0</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<PackageId>AssetRipper.TextureDecoder</PackageId>
|
||||
<PackageTags>C# Texture</PackageTags>
|
||||
<RepositoryUrl>https://github.com/AssetRipper/TextureDecoder</RepositoryUrl>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageProjectUrl>https://github.com/AssetRipper/TextureDecoder</PackageProjectUrl>
|
||||
<Copyright>Copyright (c) 2022 - 2023 ds5678</Copyright>
|
||||
<Description>Managed library for decoding textures in multiple formats.</Description>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<DocumentationFile>bin\TextureDecoder.xml</DocumentationFile>
|
||||
<NoWarn>1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="PolySharp" Version="1.13.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<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 - 2024 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>
|
||||
@@ -1,30 +1,35 @@
|
||||
namespace AssetRipper.TextureDecoder.Astc
|
||||
namespace AssetRipper.TextureDecoder.Astc;
|
||||
|
||||
public static partial class AstcDecoder
|
||||
{
|
||||
public static partial class AstcDecoder
|
||||
private struct BlockData
|
||||
{
|
||||
private unsafe struct BlockData
|
||||
{
|
||||
public int bw;
|
||||
public int bh;
|
||||
public int width;
|
||||
public int height;
|
||||
public int part_num;
|
||||
public int dual_plane;
|
||||
public int plane_selector;
|
||||
public int weight_range;
|
||||
/// <summary>
|
||||
/// max: 120
|
||||
/// </summary>
|
||||
public int weight_num;
|
||||
public fixed int cem[4];
|
||||
public int cem_range;
|
||||
/// <summary>
|
||||
/// max: 32
|
||||
/// </summary>
|
||||
public int endpoint_value_num;
|
||||
public fixed int endpoints[4 * 8];
|
||||
public fixed int weights[144 * 2];
|
||||
public fixed int partition[144];
|
||||
}
|
||||
public int bw;
|
||||
public int bh;
|
||||
public int width;
|
||||
public int height;
|
||||
public int part_num;
|
||||
public int dual_plane;
|
||||
public int plane_selector;
|
||||
public int weight_range;
|
||||
/// <summary>
|
||||
/// max: 120
|
||||
/// </summary>
|
||||
public int weight_num;
|
||||
public IntBuffer4 cem;
|
||||
public int cem_range;
|
||||
/// <summary>
|
||||
/// max: 32
|
||||
/// </summary>
|
||||
public int endpoint_value_num;
|
||||
/// <summary>
|
||||
/// 4 * 8
|
||||
/// </summary>
|
||||
public EndpointBuffer endpoints;
|
||||
/// <summary>
|
||||
/// 144 * 2
|
||||
/// </summary>
|
||||
public IntBuffer288 weights;
|
||||
public IntBuffer144 partition;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace AssetRipper.TextureDecoder.Astc;
|
||||
|
||||
public static partial class AstcDecoder
|
||||
{
|
||||
[InlineArray(4)]
|
||||
private struct EndpointBuffer
|
||||
{
|
||||
private Endpoint _element0;
|
||||
|
||||
[InlineArray(8)]
|
||||
public struct Endpoint
|
||||
{
|
||||
private int _element0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace AssetRipper.TextureDecoder.Astc;
|
||||
|
||||
public static partial class AstcDecoder
|
||||
{
|
||||
[InlineArray(144)]
|
||||
private struct IntBuffer144
|
||||
{
|
||||
private int _element0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace AssetRipper.TextureDecoder.Astc;
|
||||
|
||||
public static partial class AstcDecoder
|
||||
{
|
||||
[InlineArray(288)]
|
||||
private struct IntBuffer288
|
||||
{
|
||||
private int _element0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace AssetRipper.TextureDecoder.Astc;
|
||||
|
||||
public static partial class AstcDecoder
|
||||
{
|
||||
[InlineArray(4)]
|
||||
private struct IntBuffer4
|
||||
{
|
||||
private int _element0;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
namespace AssetRipper.TextureDecoder.Astc
|
||||
namespace AssetRipper.TextureDecoder.Astc;
|
||||
|
||||
public static partial class AstcDecoder
|
||||
{
|
||||
public static partial class AstcDecoder
|
||||
private struct IntSeqData
|
||||
{
|
||||
private struct IntSeqData
|
||||
{
|
||||
public int bits;
|
||||
public int nonbits;
|
||||
}
|
||||
public int bits;
|
||||
public int nonbits;
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static void DecodeBlock(ReadOnlySpan<byte> input, int blockWidth, int blockHeight, Span<uint> output)
|
||||
private static void DecodeBlock(ReadOnlySpan<byte> input, int blockWidth, int blockHeight, Span<uint> output)
|
||||
{
|
||||
if (input[0] == 0xfc && (input[1] & 1) == 1)
|
||||
{
|
||||
@@ -78,7 +78,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static void DecodeBlockParameters(ReadOnlySpan<byte> input, ref BlockData pBlock)
|
||||
private static void DecodeBlockParameters(ReadOnlySpan<byte> input, ref BlockData pBlock)
|
||||
{
|
||||
pBlock.dual_plane = (input[1] & 4) >> 2;
|
||||
pBlock.weight_range = (input[0] >> 4 & 1) | (input[1] << 2 & 8);
|
||||
@@ -231,7 +231,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static void DecodeEndpoints(ReadOnlySpan<byte> input, ref BlockData pBlock)
|
||||
private static void DecodeEndpoints(ReadOnlySpan<byte> input, ref BlockData pBlock)
|
||||
{
|
||||
Span<IntSeqData> epSeq = stackalloc IntSeqData[32];
|
||||
DecodeIntseq(input, pBlock.part_num == 1 ? 17 : 29, CemTableA[pBlock.cem_range], CemTableB[pBlock.cem_range], pBlock.endpoint_value_num, false, epSeq);
|
||||
@@ -352,61 +352,40 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
}
|
||||
|
||||
Span<int> v = ev;
|
||||
for (int cem = 0, cemOff = 0; cem < pBlock.part_num; v = v.Slice((pBlock.cem[cem] / 4 + 1) * 2), cem++, cemOff += 8)
|
||||
for (int cem = 0; cem < pBlock.part_num; v = v.Slice((pBlock.cem[cem] / 4 + 1) * 2), cem++)
|
||||
{
|
||||
switch (pBlock.cem[cem])
|
||||
{
|
||||
case 0:
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpoint(new Span<int>(endpoint, 8), v[0], v[0], v[0], 255, v[1], v[1], v[1], 255);
|
||||
}
|
||||
SetEndpoint(pBlock.endpoints[cem], v[0], v[0], v[0], 255, v[1], v[1], v[1], 255);
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
int l0 = (v[0] >> 2) | (v[1] & 0xc0);
|
||||
int l1 = Clamp(l0 + (v[1] & 0x3f));
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpoint(new Span<int>(endpoint, 8), l0, l0, l0, 255, l1, l1, l1, 255);
|
||||
}
|
||||
SetEndpoint(pBlock.endpoints[cem], l0, l0, l0, 255, l1, l1, l1, 255);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpoint(new Span<int>(endpoint, 8), v[0], v[0], v[0], v[2], v[1], v[1], v[1], v[3]);
|
||||
}
|
||||
SetEndpoint(pBlock.endpoints[cem], v[0], v[0], v[0], v[2], v[1], v[1], v[1], v[3]);
|
||||
break;
|
||||
case 5:
|
||||
BitTransferSigned(ref v[1], ref v[0]);
|
||||
BitTransferSigned(ref v[3], ref v[2]);
|
||||
v[1] += v[0];
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpointClamp(new Span<int>(endpoint, 8), v[0], v[0], v[0], v[2], v[1], v[1], v[1], v[2] + v[3]);
|
||||
}
|
||||
SetEndpointClamp(pBlock.endpoints[cem], v[0], v[0], v[0], v[2], v[1], v[1], v[1], v[2] + v[3]);
|
||||
break;
|
||||
case 6:
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpoint(new Span<int>(endpoint, 8), v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8, 255, v[0], v[1], v[2], 255);
|
||||
}
|
||||
SetEndpoint(pBlock.endpoints[cem], v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8, 255, v[0], v[1], v[2], 255);
|
||||
break;
|
||||
case 8:
|
||||
if (v[0] + v[2] + v[4] <= v[1] + v[3] + v[5])
|
||||
{
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpoint(new Span<int>(endpoint, 8), v[0], v[2], v[4], 255, v[1], v[3], v[5], 255);
|
||||
}
|
||||
SetEndpoint(pBlock.endpoints[cem], v[0], v[2], v[4], 255, v[1], v[3], v[5], 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpointBlue(new Span<int>(endpoint, 8), v[1], v[3], v[5], 255, v[0], v[2], v[4], 255);
|
||||
}
|
||||
SetEndpointBlue(pBlock.endpoints[cem], v[1], v[3], v[5], 255, v[0], v[2], v[4], 255);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -416,40 +395,25 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
BitTransferSigned(ref v[5], ref v[4]);
|
||||
if (v[1] + v[3] + v[5] >= 0)
|
||||
{
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpointClamp(new Span<int>(endpoint, 8), v[0], v[2], v[4], 255, v[0] + v[1], v[2] + v[3], v[4] + v[5], 255);
|
||||
}
|
||||
SetEndpointClamp(pBlock.endpoints[cem], v[0], v[2], v[4], 255, v[0] + v[1], v[2] + v[3], v[4] + v[5], 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpointBlueClamp(new Span<int>(endpoint, 8), v[0] + v[1], v[2] + v[3], v[4] + v[5], 255, v[0], v[2], v[4], 255);
|
||||
}
|
||||
SetEndpointBlueClamp(pBlock.endpoints[cem], v[0] + v[1], v[2] + v[3], v[4] + v[5], 255, v[0], v[2], v[4], 255);
|
||||
}
|
||||
|
||||
break;
|
||||
case 10:
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpoint(new Span<int>(endpoint, 8), v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8, v[4], v[0], v[1], v[2], v[5]);
|
||||
}
|
||||
SetEndpoint(pBlock.endpoints[cem], v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8, v[4], v[0], v[1], v[2], v[5]);
|
||||
break;
|
||||
case 12:
|
||||
if (v[0] + v[2] + v[4] <= v[1] + v[3] + v[5])
|
||||
{
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpoint(new Span<int>(endpoint, 8), v[0], v[2], v[4], v[6], v[1], v[3], v[5], v[7]);
|
||||
}
|
||||
SetEndpoint(pBlock.endpoints[cem], v[0], v[2], v[4], v[6], v[1], v[3], v[5], v[7]);
|
||||
}
|
||||
else
|
||||
{
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpointBlue(new Span<int>(endpoint, 8), v[1], v[3], v[5], v[7], v[0], v[2], v[4], v[6]);
|
||||
}
|
||||
SetEndpointBlue(pBlock.endpoints[cem], v[1], v[3], v[5], v[7], v[0], v[2], v[4], v[6]);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -460,17 +424,11 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
BitTransferSigned(ref v[7], ref v[6]);
|
||||
if (v[1] + v[3] + v[5] >= 0)
|
||||
{
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpointClamp(new Span<int>(endpoint, 8), v[0], v[2], v[4], v[6], v[0] + v[1], v[2] + v[3], v[4] + v[5], v[6] + v[7]);
|
||||
}
|
||||
SetEndpointClamp(pBlock.endpoints[cem], v[0], v[2], v[4], v[6], v[0] + v[1], v[2] + v[3], v[4] + v[5], v[6] + v[7]);
|
||||
}
|
||||
else
|
||||
{
|
||||
fixed (int* endpoint = &pBlock.endpoints[cemOff])
|
||||
{
|
||||
SetEndpointBlueClamp(new Span<int>(endpoint, 8), v[0] + v[1], v[2] + v[3], v[4] + v[5], v[6] + v[7], v[0], v[2], v[4], v[6]);
|
||||
}
|
||||
SetEndpointBlueClamp(pBlock.endpoints[cem], v[0] + v[1], v[2] + v[3], v[4] + v[5], v[6] + v[7], v[0], v[2], v[4], v[6]);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -478,7 +436,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static void DecodeWeights(ReadOnlySpan<byte> input, ref BlockData block)
|
||||
private static void DecodeWeights(ReadOnlySpan<byte> input, ref BlockData block)
|
||||
{
|
||||
Span<IntSeqData> wSeq = stackalloc IntSeqData[128];
|
||||
DecodeIntseq(input, 128, WeightPrecTableA[block.weight_range], WeightPrecTableB[block.weight_range], block.weight_num, true, wSeq);
|
||||
@@ -634,7 +592,7 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static void SelectPartition(ReadOnlySpan<byte> input, ref BlockData block)
|
||||
private static void SelectPartition(ReadOnlySpan<byte> input, ref BlockData block)
|
||||
{
|
||||
bool small_block = block.bw * block.bh < 31;
|
||||
int seed = (BinaryPrimitives.ReadInt32LittleEndian(input) >> 13 & 0x3ff) | (block.part_num - 1) << 10;
|
||||
@@ -663,11 +621,11 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
}
|
||||
|
||||
|
||||
ReadOnlySpan<int> sh = stackalloc int[2]
|
||||
{
|
||||
ReadOnlySpan<int> sh =
|
||||
[
|
||||
(seed & 2) != 0 ? 4 : 5,
|
||||
block.part_num == 3 ? 6 : 5
|
||||
};
|
||||
];
|
||||
|
||||
if ((seed & 1) != 0)
|
||||
{
|
||||
@@ -716,21 +674,21 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static void ApplicateColor(BlockData block, Span<uint> output)
|
||||
private static void ApplicateColor(BlockData block, Span<uint> output)
|
||||
{
|
||||
if (block.dual_plane != 0)
|
||||
{
|
||||
Span<int> ps = stackalloc int[] { 0, 0, 0, 0 };
|
||||
Span<int> ps = [0, 0, 0, 0];
|
||||
ps[block.plane_selector] = 1;
|
||||
if (block.part_num > 1)
|
||||
{
|
||||
for (int i = 0; i < block.bw * block.bh; i++)
|
||||
{
|
||||
int p = block.partition[i];
|
||||
byte r = SelectColor(block.endpoints[p * 8 + 0], block.endpoints[p * 8 + 4], block.weights[i * 2 + ps[0]]);
|
||||
byte g = SelectColor(block.endpoints[p * 8 + 1], block.endpoints[p * 8 + 5], block.weights[i * 2 + ps[1]]);
|
||||
byte b = SelectColor(block.endpoints[p * 8 + 2], block.endpoints[p * 8 + 6], block.weights[i * 2 + ps[2]]);
|
||||
byte a = SelectColor(block.endpoints[p * 8 + 3], block.endpoints[p * 8 + 7], block.weights[i * 2 + ps[3]]);
|
||||
byte r = SelectColor(block.endpoints[p][0], block.endpoints[p][4], block.weights[i * 2 + ps[0]]);
|
||||
byte g = SelectColor(block.endpoints[p][1], block.endpoints[p][5], block.weights[i * 2 + ps[1]]);
|
||||
byte b = SelectColor(block.endpoints[p][2], block.endpoints[p][6], block.weights[i * 2 + ps[2]]);
|
||||
byte a = SelectColor(block.endpoints[p][3], block.endpoints[p][7], block.weights[i * 2 + ps[3]]);
|
||||
output[i] = Color(r, g, b, a);
|
||||
}
|
||||
}
|
||||
@@ -738,10 +696,10 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
{
|
||||
for (int i = 0; i < block.bw * block.bh; i++)
|
||||
{
|
||||
byte r = SelectColor(block.endpoints[0], block.endpoints[4], block.weights[i * 2 + ps[0]]);
|
||||
byte g = SelectColor(block.endpoints[1], block.endpoints[5], block.weights[i * 2 + ps[1]]);
|
||||
byte b = SelectColor(block.endpoints[2], block.endpoints[6], block.weights[i * 2 + ps[2]]);
|
||||
byte a = SelectColor(block.endpoints[3], block.endpoints[7], block.weights[i * 2 + ps[3]]);
|
||||
byte r = SelectColor(block.endpoints[0][0], block.endpoints[0][4], block.weights[i * 2 + ps[0]]);
|
||||
byte g = SelectColor(block.endpoints[0][1], block.endpoints[0][5], block.weights[i * 2 + ps[1]]);
|
||||
byte b = SelectColor(block.endpoints[0][2], block.endpoints[0][6], block.weights[i * 2 + ps[2]]);
|
||||
byte a = SelectColor(block.endpoints[0][3], block.endpoints[0][7], block.weights[i * 2 + ps[3]]);
|
||||
output[i] = Color(r, g, b, a);
|
||||
}
|
||||
}
|
||||
@@ -751,10 +709,10 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
for (int i = 0; i < block.bw * block.bh; i++)
|
||||
{
|
||||
int p = block.partition[i];
|
||||
byte r = SelectColor(block.endpoints[p * 8 + 0], block.endpoints[p * 8 + 4], block.weights[i * 2]);
|
||||
byte g = SelectColor(block.endpoints[p * 8 + 1], block.endpoints[p * 8 + 5], block.weights[i * 2]);
|
||||
byte b = SelectColor(block.endpoints[p * 8 + 2], block.endpoints[p * 8 + 6], block.weights[i * 2]);
|
||||
byte a = SelectColor(block.endpoints[p * 8 + 3], block.endpoints[p * 8 + 7], block.weights[i * 2]);
|
||||
byte r = SelectColor(block.endpoints[p][0], block.endpoints[p][4], block.weights[i * 2]);
|
||||
byte g = SelectColor(block.endpoints[p][1], block.endpoints[p][5], block.weights[i * 2]);
|
||||
byte b = SelectColor(block.endpoints[p][2], block.endpoints[p][6], block.weights[i * 2]);
|
||||
byte a = SelectColor(block.endpoints[p][3], block.endpoints[p][7], block.weights[i * 2]);
|
||||
output[i] = Color(r, g, b, a);
|
||||
}
|
||||
}
|
||||
@@ -762,10 +720,10 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
{
|
||||
for (int i = 0; i < block.bw * block.bh; i++)
|
||||
{
|
||||
byte r = SelectColor(block.endpoints[0], block.endpoints[4], block.weights[i * 2]);
|
||||
byte g = SelectColor(block.endpoints[1], block.endpoints[5], block.weights[i * 2]);
|
||||
byte b = SelectColor(block.endpoints[2], block.endpoints[6], block.weights[i * 2]);
|
||||
byte a = SelectColor(block.endpoints[3], block.endpoints[7], block.weights[i * 2]);
|
||||
byte r = SelectColor(block.endpoints[0][0], block.endpoints[0][4], block.weights[i * 2]);
|
||||
byte g = SelectColor(block.endpoints[0][1], block.endpoints[0][5], block.weights[i * 2]);
|
||||
byte b = SelectColor(block.endpoints[0][2], block.endpoints[0][6], block.weights[i * 2]);
|
||||
byte a = SelectColor(block.endpoints[0][3], block.endpoints[0][7], block.weights[i * 2]);
|
||||
output[i] = Color(r, g, b, a);
|
||||
}
|
||||
}
|
||||
@@ -925,7 +883,19 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static int GetBits(ReadOnlySpan<byte> input, int bit, int len)
|
||||
{
|
||||
return (BinaryPrimitives.ReadInt32LittleEndian(input[(bit / 8)..]) >> (bit % 8)) & ((1 << len) - 1);
|
||||
ReadOnlySpan<byte> slice = input[(bit / 8)..];
|
||||
int bitBuffer;
|
||||
if (slice.Length >= sizeof(int))
|
||||
{
|
||||
bitBuffer = BinaryPrimitives.ReadInt32LittleEndian(slice);
|
||||
}
|
||||
else
|
||||
{
|
||||
Span<byte> temp = stackalloc byte[sizeof(int)];
|
||||
slice.CopyTo(temp);
|
||||
bitBuffer = BinaryPrimitives.ReadInt32LittleEndian(temp);
|
||||
}
|
||||
return (bitBuffer >> (bit % 8)) & ((1 << len) - 1);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
@@ -1029,8 +999,8 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
return (byte)(((((v0 << 8 | v0) * (64 - weight) + (v1 << 8 | v1) * weight + 32) >> 6) * 255 + 32768) / 65536);
|
||||
}
|
||||
|
||||
private static readonly byte[] BitReverseTable = new byte[]
|
||||
{
|
||||
private static readonly byte[] BitReverseTable =
|
||||
[
|
||||
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
|
||||
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
|
||||
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
|
||||
@@ -1047,32 +1017,32 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
|
||||
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
|
||||
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
|
||||
};
|
||||
];
|
||||
|
||||
private static readonly int[] WeightPrecTableA = new int[] { 0, 0, 0, 3, 0, 5, 3, 0, 0, 0, 5, 3, 0, 5, 3, 0 };
|
||||
private static readonly int[] WeightPrecTableB = new int[] { 0, 0, 1, 0, 2, 0, 1, 3, 0, 0, 1, 2, 4, 2, 3, 5 };
|
||||
private static 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 readonly int[] CemTableA = new int[] { 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 0, 0 };
|
||||
private static readonly int[] CemTableB = new int[] { 8, 6, 5, 7, 5, 4, 6, 4, 3, 5, 3, 2, 4, 2, 1, 3, 1, 2, 1 };
|
||||
private static 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 readonly int[] DImt = new int[] { 0, 2, 4, 5, 7 };
|
||||
private static readonly int[] DImq = new int[] { 0, 3, 5 };
|
||||
private static readonly int[] DImt = [0, 2, 4, 5, 7];
|
||||
private static readonly int[] DImq = [0, 3, 5];
|
||||
private static readonly int[] DITritsTable =
|
||||
{
|
||||
[
|
||||
0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 1, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 1, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2,
|
||||
0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 1, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 1,
|
||||
0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 2, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 2, 2,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
|
||||
};
|
||||
];
|
||||
private static readonly int[] DIQuintsTable =
|
||||
{
|
||||
[
|
||||
0, 1, 2, 3, 4, 0, 4, 4, 0, 1, 2, 3, 4, 1, 4, 4, 0, 1, 2, 3, 4, 2, 4, 4, 0, 1, 2, 3, 4, 3, 4, 4, 0, 1, 2, 3, 4, 0, 4, 0, 0, 1, 2, 3, 4, 1, 4, 1, 0, 1, 2, 3, 4, 2, 4, 2, 0, 1, 2, 3, 4, 3, 4, 3, 0, 1, 2, 3, 4, 0, 2, 3, 0, 1, 2, 3, 4, 1, 2, 3, 0, 1, 2, 3, 4, 2, 2, 3, 0, 1, 2, 3, 4, 3, 2, 3, 0, 1, 2, 3, 4, 0, 0, 1, 0, 1, 2, 3, 4, 1, 0, 1, 0, 1, 2, 3, 4, 2, 0, 1, 0, 1, 2, 3, 4, 3, 0, 1,
|
||||
0, 0, 0, 0, 0, 4, 4, 4, 1, 1, 1, 1, 1, 4, 4, 4, 2, 2, 2, 2, 2, 4, 4, 4, 3, 3, 3, 3, 3, 4, 4, 4, 0, 0, 0, 0, 0, 4, 0, 4, 1, 1, 1, 1, 1, 4, 1, 4, 2, 2, 2, 2, 2, 4, 2, 4, 3, 3, 3, 3, 3, 4, 3, 4, 0, 0, 0, 0, 0, 4, 0, 0, 1, 1, 1, 1, 1, 4, 1, 1, 2, 2, 2, 2, 2, 4, 2, 2, 3, 3, 3, 3, 3, 4, 3, 3, 0, 0, 0, 0, 0, 4, 0, 0, 1, 1, 1, 1, 1, 4, 1, 1, 2, 2, 2, 2, 2, 4, 2, 2, 3, 3, 3, 3, 3, 4, 3, 3,
|
||||
0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 1, 4, 0, 0, 0, 0, 0, 0, 2, 4, 0, 0, 0, 0, 0, 0, 3, 4, 1, 1, 1, 1, 1, 1, 4, 4, 1, 1, 1, 1, 1, 1, 4, 4, 1, 1, 1, 1, 1, 1, 4, 4, 1, 1, 1, 1, 1, 1, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4,
|
||||
};
|
||||
];
|
||||
|
||||
private static readonly int[] DETritsTable = new int[] { 0, 204, 93, 44, 22, 11, 5 };
|
||||
private static readonly int[] DEQuintsTable = new int[] { 0, 113, 54, 26, 13, 6 };
|
||||
private static readonly int[] DETritsTable = [0, 204, 93, 44, 22, 11, 5];
|
||||
private static readonly int[] DEQuintsTable = [0, 113, 54, 26, 13, 6];
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Buffers.Binary;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Atc
|
||||
{
|
||||
|
||||
@@ -5,7 +5,10 @@ namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
public static class Bc1
|
||||
{
|
||||
internal const int BlockSize = 8;
|
||||
/// <summary>
|
||||
/// The size of an encoded block, in bytes.
|
||||
/// </summary>
|
||||
public const int BlockSize = 8;
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
@@ -30,4 +33,4 @@ public static class Bc1
|
||||
}
|
||||
|
||||
internal static int GetCompressedSize(int w, int h) => ((w) >> 2) * ((h) >> 2) * BlockSize;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,10 @@ namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
public static class Bc2
|
||||
{
|
||||
internal const int BlockSize = 16;
|
||||
/// <summary>
|
||||
/// The size of an encoded block, in bytes.
|
||||
/// </summary>
|
||||
public const int BlockSize = 16;
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
@@ -30,4 +33,4 @@ public static class Bc2
|
||||
}
|
||||
|
||||
internal static int GetCompressedSize(int w, int h) => ((w) >> 2) * ((h) >> 2) * BlockSize;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,10 @@ namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
public static class Bc3
|
||||
{
|
||||
internal const int BlockSize = 16;
|
||||
/// <summary>
|
||||
/// The size of an encoded block, in bytes.
|
||||
/// </summary>
|
||||
public const int BlockSize = 16;
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
@@ -30,4 +33,4 @@ public static class Bc3
|
||||
}
|
||||
|
||||
internal static int GetCompressedSize(int w, int h) => ((w) >> 2) * ((h) >> 2) * BlockSize;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,10 @@ namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
public static class Bc4
|
||||
{
|
||||
internal const int BlockSize = 8;
|
||||
/// <summary>
|
||||
/// The size of an encoded block, in bytes.
|
||||
/// </summary>
|
||||
public const int BlockSize = 8;
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
@@ -35,4 +38,4 @@ public static class Bc4
|
||||
}
|
||||
|
||||
internal static int GetCompressedSize(int w, int h) => ((w) >> 2) * ((h) >> 2) * BlockSize;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,10 @@ namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
public static class Bc5
|
||||
{
|
||||
internal const int BlockSize = 16;
|
||||
/// <summary>
|
||||
/// The size of an encoded block, in bytes.
|
||||
/// </summary>
|
||||
public const int BlockSize = 16;
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
@@ -35,4 +38,4 @@ public static class Bc5
|
||||
}
|
||||
|
||||
internal static int GetCompressedSize(int w, int h) => ((w) >> 2) * ((h) >> 2) * BlockSize;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,10 @@ namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
public static class Bc6h
|
||||
{
|
||||
internal const int BlockSize = 16;
|
||||
/// <summary>
|
||||
/// The size of an encoded block, in bytes.
|
||||
/// </summary>
|
||||
public const int BlockSize = 16;
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, bool isSigned, out byte[] output)
|
||||
{
|
||||
@@ -39,4 +42,4 @@ public static class Bc6h
|
||||
}
|
||||
|
||||
internal static int GetCompressedSize(int w, int h) => ((w) >> 2) * ((h) >> 2) * BlockSize;
|
||||
}
|
||||
}
|
||||
@@ -2,248 +2,216 @@
|
||||
|
||||
internal static class Bc6hTables
|
||||
{
|
||||
internal static byte[][] ActualBitsCount { get; } = new byte[4][]
|
||||
{
|
||||
new byte[14] {10, 7, 11, 11, 11, 9, 8, 8, 8, 6, 10, 11, 12, 16},
|
||||
new byte[14] {5, 6, 5, 4, 4, 5, 6, 5, 5, 6, 10, 9, 8, 4},
|
||||
new byte[14] {5, 6, 4, 5, 4, 5, 5, 6, 5, 6, 10, 9, 8, 4},
|
||||
new byte[14] {5, 6, 4, 4, 5, 5, 5, 5, 6, 6, 10, 9, 8, 4}
|
||||
};
|
||||
internal static byte[][] ActualBitsCount { get; } =
|
||||
[
|
||||
[10, 7, 11, 11, 11, 9, 8, 8, 8, 6, 10, 11, 12, 16],
|
||||
[5, 6, 5, 4, 4, 5, 6, 5, 5, 6, 10, 9, 8, 4],
|
||||
[5, 6, 4, 5, 4, 5, 5, 6, 5, 6, 10, 9, 8, 4],
|
||||
[5, 6, 4, 4, 5, 5, 5, 5, 6, 6, 10, 9, 8, 4]
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// There are 32 possible partition sets for a two-region tile.
|
||||
/// Each 4x4 block represents a single shape.
|
||||
/// Here also every fix-up index has MSB bit set.
|
||||
/// </summary>
|
||||
internal static byte[][][] PartitionSets { get; } = new byte[32][][]
|
||||
{
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 1, 1},
|
||||
new byte[4] {0, 0, 1, 1},
|
||||
new byte[4] {0, 0, 1, 1},
|
||||
new byte[4] {0, 0, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 1},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 0, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 1, 1, 1},
|
||||
new byte[4] {0, 1, 1, 1},
|
||||
new byte[4] {0, 1, 1, 1},
|
||||
new byte[4] {0, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 1},
|
||||
new byte[4] {0, 0, 1, 1},
|
||||
new byte[4] {0, 0, 1, 1},
|
||||
new byte[4] {0, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 1, 1},
|
||||
new byte[4] {0, 1, 1, 1},
|
||||
new byte[4] {0, 1, 1, 1},
|
||||
new byte[4] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 1},
|
||||
new byte[4] {0, 0, 1, 1},
|
||||
new byte[4] {0, 1, 1, 1},
|
||||
new byte[4] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 1, 1},
|
||||
new byte[4] {0, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 1, 1},
|
||||
new byte[4] {0, 1, 1, 1},
|
||||
new byte[4] {1, 1, 1, 1},
|
||||
new byte[4] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 1, 1, 1},
|
||||
new byte[4] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 1},
|
||||
new byte[4] {0, 1, 1, 1},
|
||||
new byte[4] {1, 1, 1, 1},
|
||||
new byte[4] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 0},
|
||||
new byte[4] {1, 1, 1, 1},
|
||||
new byte[4] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {1, 1, 1, 1},
|
||||
new byte[4] {1, 1, 1, 1},
|
||||
new byte[4] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 0},
|
||||
new byte[4] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {1, 0, 0, 0},
|
||||
new byte[4] {1, 1, 1, 0},
|
||||
new byte[4] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 1, 129, 1},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 0},
|
||||
new byte[4] {129, 0, 0, 0},
|
||||
new byte[4] {1, 1, 1, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 1, 129, 1},
|
||||
new byte[4] {0, 0, 1, 1},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 129, 1},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {1, 0, 0, 0},
|
||||
new byte[4] {129, 1, 0, 0},
|
||||
new byte[4] {1, 1, 1, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {0, 0, 0, 0},
|
||||
new byte[4] {129, 0, 0, 0},
|
||||
new byte[4] {1, 1, 0, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 1, 1, 1},
|
||||
new byte[4] {0, 0, 1, 1},
|
||||
new byte[4] {0, 0, 1, 1},
|
||||
new byte[4] {0, 0, 0, 129}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 129, 1},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {1, 0, 0, 0},
|
||||
new byte[4] {129, 0, 0, 0},
|
||||
new byte[4] {1, 1, 0, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 1, 129, 0},
|
||||
new byte[4] {0, 1, 1, 0},
|
||||
new byte[4] {0, 1, 1, 0},
|
||||
new byte[4] {0, 1, 1, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 129, 1},
|
||||
new byte[4] {0, 1, 1, 0},
|
||||
new byte[4] {0, 1, 1, 0},
|
||||
new byte[4] {1, 1, 0, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 1},
|
||||
new byte[4] {0, 1, 1, 1},
|
||||
new byte[4] {129, 1, 1, 0},
|
||||
new byte[4] {1, 0, 0, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 0, 0},
|
||||
new byte[4] {1, 1, 1, 1},
|
||||
new byte[4] {129, 1, 1, 1},
|
||||
new byte[4] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 1, 129, 1},
|
||||
new byte[4] {0, 0, 0, 1},
|
||||
new byte[4] {1, 0, 0, 0},
|
||||
new byte[4] {1, 1, 1, 0}
|
||||
},
|
||||
new byte[4][]
|
||||
{
|
||||
new byte[4] {128, 0, 129, 1},
|
||||
new byte[4] {1, 0, 0, 1},
|
||||
new byte[4] {1, 0, 0, 1},
|
||||
new byte[4] {1, 1, 0, 0}
|
||||
}
|
||||
};
|
||||
internal static byte[][][] PartitionSets { get; } =
|
||||
[
|
||||
[
|
||||
[128, 0, 1, 1],
|
||||
[0, 0, 1, 1],
|
||||
[0, 0, 1, 1],
|
||||
[0, 0, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 129]
|
||||
],
|
||||
[
|
||||
[128, 1, 1, 1],
|
||||
[0, 1, 1, 1],
|
||||
[0, 1, 1, 1],
|
||||
[0, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 1],
|
||||
[0, 0, 1, 1],
|
||||
[0, 0, 1, 1],
|
||||
[0, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 1, 1],
|
||||
[0, 1, 1, 1],
|
||||
[0, 1, 1, 1],
|
||||
[1, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 1],
|
||||
[0, 0, 1, 1],
|
||||
[0, 1, 1, 1],
|
||||
[1, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 1, 1],
|
||||
[0, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[0, 0, 0, 0],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 1, 1],
|
||||
[0, 1, 1, 1],
|
||||
[1, 1, 1, 1],
|
||||
[1, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[0, 0, 0, 1],
|
||||
[0, 1, 1, 1],
|
||||
[1, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[0, 0, 0, 0],
|
||||
[0, 0, 0, 1],
|
||||
[0, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 1],
|
||||
[0, 1, 1, 1],
|
||||
[1, 1, 1, 1],
|
||||
[1, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[0, 0, 0, 0],
|
||||
[1, 1, 1, 1],
|
||||
[1, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[1, 1, 1, 1],
|
||||
[1, 1, 1, 1],
|
||||
[1, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[0, 0, 0, 0],
|
||||
[0, 0, 0, 0],
|
||||
[1, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[1, 0, 0, 0],
|
||||
[1, 1, 1, 0],
|
||||
[1, 1, 1, 129]
|
||||
],
|
||||
[
|
||||
[128, 1, 129, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 0],
|
||||
[0, 0, 0, 0]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[0, 0, 0, 0],
|
||||
[129, 0, 0, 0],
|
||||
[1, 1, 1, 0]
|
||||
],
|
||||
[
|
||||
[128, 1, 129, 1],
|
||||
[0, 0, 1, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 0]
|
||||
],
|
||||
[
|
||||
[128, 0, 129, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 0],
|
||||
[0, 0, 0, 0]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[1, 0, 0, 0],
|
||||
[129, 1, 0, 0],
|
||||
[1, 1, 1, 0]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[0, 0, 0, 0],
|
||||
[129, 0, 0, 0],
|
||||
[1, 1, 0, 0]
|
||||
],
|
||||
[
|
||||
[128, 1, 1, 1],
|
||||
[0, 0, 1, 1],
|
||||
[0, 0, 1, 1],
|
||||
[0, 0, 0, 129]
|
||||
],
|
||||
[
|
||||
[128, 0, 129, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 1],
|
||||
[0, 0, 0, 0]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[1, 0, 0, 0],
|
||||
[129, 0, 0, 0],
|
||||
[1, 1, 0, 0]
|
||||
],
|
||||
[
|
||||
[128, 1, 129, 0],
|
||||
[0, 1, 1, 0],
|
||||
[0, 1, 1, 0],
|
||||
[0, 1, 1, 0]
|
||||
],
|
||||
[
|
||||
[128, 0, 129, 1],
|
||||
[0, 1, 1, 0],
|
||||
[0, 1, 1, 0],
|
||||
[1, 1, 0, 0]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 1],
|
||||
[0, 1, 1, 1],
|
||||
[129, 1, 1, 0],
|
||||
[1, 0, 0, 0]
|
||||
],
|
||||
[
|
||||
[128, 0, 0, 0],
|
||||
[1, 1, 1, 1],
|
||||
[129, 1, 1, 1],
|
||||
[0, 0, 0, 0]
|
||||
],
|
||||
[
|
||||
[128, 1, 129, 1],
|
||||
[0, 0, 0, 1],
|
||||
[1, 0, 0, 0],
|
||||
[1, 1, 1, 0]
|
||||
],
|
||||
[
|
||||
[128, 0, 129, 1],
|
||||
[1, 0, 0, 1],
|
||||
[1, 0, 0, 1],
|
||||
[1, 1, 0, 0]
|
||||
]
|
||||
];
|
||||
|
||||
internal static int[] AWeight3 { get; } = new int[8] { 0, 9, 18, 27, 37, 46, 55, 64 };
|
||||
internal static int[] AWeight3 { get; } = [0, 9, 18, 27, 37, 46, 55, 64];
|
||||
|
||||
internal static int[] AWeight4 { get; } = new int[16] { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 };
|
||||
internal static int[] AWeight4 { get; } = [0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64];
|
||||
}
|
||||
@@ -5,7 +5,22 @@ namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
public static class Bc7
|
||||
{
|
||||
internal const int BlockSize = 16;
|
||||
/// <summary>
|
||||
/// The size of an encoded block, in bytes.
|
||||
/// </summary>
|
||||
public const int BlockSize = 16;
|
||||
/// <summary>
|
||||
/// The width of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockWidth = 4;
|
||||
/// <summary>
|
||||
/// The height of a decoded block, in pixels.
|
||||
/// </summary>
|
||||
private const int BlockHeight = 4;
|
||||
/// <summary>
|
||||
/// The size of the natural pixel type.
|
||||
/// </summary>
|
||||
private static int PixelSize => Unsafe.SizeOf<ColorRGBA<byte>>();
|
||||
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
@@ -16,13 +31,13 @@ public static class Bc7
|
||||
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
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>>();
|
||||
int outputOffset = ((i * width) + j) * PixelSize;
|
||||
BcHelpers.DecompressBc7(
|
||||
input.Slice(inputOffset, BlockSize),
|
||||
input.Slice(inputOffset, BlockSize),
|
||||
output.Slice(outputOffset),
|
||||
width * 4);
|
||||
inputOffset += BlockSize;
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -564,8 +564,8 @@ internal static class BcHelpers
|
||||
low = BinaryPrimitives.ReadUInt64LittleEndian(compressedBlock),
|
||||
high = BinaryPrimitives.ReadUInt64LittleEndian(compressedBlock.Slice(sizeof(ulong)))
|
||||
};
|
||||
int[][] endpoints = CreateRectangularArray<int>(6, 4);
|
||||
int[][] indices = CreateRectangularArray<int>(4, 4);
|
||||
Span2D<int> endpoints = new(stackalloc int[6 * 4], 6, 4);
|
||||
Span2D<int> indices = new(stackalloc int[4 * 4], 4, 4);
|
||||
|
||||
int decompressedOffset = 0;
|
||||
|
||||
@@ -618,7 +618,7 @@ internal static class BcHelpers
|
||||
{
|
||||
for (int j = 0; j < numEndpoints; ++j)
|
||||
{
|
||||
endpoints[j][i] = bstream.ReadBits(Bc7Tables.ActualBitsCount[0][mode]);
|
||||
endpoints[j, i] = bstream.ReadBits(Bc7Tables.ActualBitsCount[0][mode]);
|
||||
}
|
||||
}
|
||||
// Alpha (if any)
|
||||
@@ -626,7 +626,7 @@ internal static class BcHelpers
|
||||
{
|
||||
for (int j = 0; j < numEndpoints; ++j)
|
||||
{
|
||||
endpoints[j][3] = bstream.ReadBits(Bc7Tables.ActualBitsCount[1][mode]);
|
||||
endpoints[j, 3] = bstream.ReadBits(Bc7Tables.ActualBitsCount[1][mode]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -639,7 +639,7 @@ internal static class BcHelpers
|
||||
// component-wise left-shift
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
endpoints[i][j] <<= 1;
|
||||
endpoints[i, j] <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -652,10 +652,10 @@ internal static class BcHelpers
|
||||
// rgb component-wise insert pbits
|
||||
for (int k = 0; k < 3; ++k)
|
||||
{
|
||||
endpoints[0][k] |= i;
|
||||
endpoints[1][k] |= i;
|
||||
endpoints[2][k] |= j;
|
||||
endpoints[3][k] |= j;
|
||||
endpoints[0, k] |= i;
|
||||
endpoints[1, k] |= i;
|
||||
endpoints[2, k] |= j;
|
||||
endpoints[3, k] |= j;
|
||||
}
|
||||
}
|
||||
else if ((Bc7Tables.bcdec_bc7_sModeHasPBits & (1 << mode)) != 0)
|
||||
@@ -666,7 +666,7 @@ internal static class BcHelpers
|
||||
int j = bstream.ReadBit();
|
||||
for (int k = 0; k < 4; ++k)
|
||||
{
|
||||
endpoints[i][k] |= j;
|
||||
endpoints[i, k] |= j;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -680,18 +680,18 @@ internal static class BcHelpers
|
||||
for (int k = 0; k < 3; ++k)
|
||||
{
|
||||
// left shift endpoint components so that their MSB lies in bit 7
|
||||
endpoints[i][k] = endpoints[i][k] << (8 - j);
|
||||
endpoints[i, k] = endpoints[i, k] << (8 - j);
|
||||
// Replicate each component's MSB into the LSBs revealed by the left-shift operation above
|
||||
endpoints[i][k] = endpoints[i][k] | (endpoints[i][k] >> j);
|
||||
endpoints[i, k] = endpoints[i, k] | (endpoints[i, k] >> j);
|
||||
}
|
||||
|
||||
// get alpha component precision including pbit
|
||||
j = Bc7Tables.ActualBitsCount[1][mode] + ((Bc7Tables.bcdec_bc7_sModeHasPBits >> mode) & 1);
|
||||
|
||||
// left shift endpoint components so that their MSB lies in bit 7
|
||||
endpoints[i][3] = endpoints[i][3] << (8 - j);
|
||||
endpoints[i, 3] = endpoints[i, 3] << (8 - j);
|
||||
// Replicate each component's MSB into the LSBs revealed by the left-shift operation above
|
||||
endpoints[i][3] = endpoints[i][3] | (endpoints[i][3] >> j);
|
||||
endpoints[i, 3] = endpoints[i, 3] | (endpoints[i, 3] >> j);
|
||||
}
|
||||
|
||||
// If this mode does not explicitly define the alpha component
|
||||
@@ -700,7 +700,7 @@ internal static class BcHelpers
|
||||
{
|
||||
for (int j = 0; j < numEndpoints; ++j)
|
||||
{
|
||||
endpoints[j][3] = 0xFF;
|
||||
endpoints[j, 3] = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -726,7 +726,7 @@ internal static class BcHelpers
|
||||
indexBits--;
|
||||
}
|
||||
|
||||
indices[i][j] = bstream.ReadBits(indexBits);
|
||||
indices[i, j] = bstream.ReadBits(indexBits);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -740,7 +740,7 @@ internal static class BcHelpers
|
||||
: Bc7Tables.PartitionSets[numPartitions - 2][partition][i][j];
|
||||
partitionSet &= 0x03;
|
||||
|
||||
int index = indices[i][j];
|
||||
int index = indices[i, j];
|
||||
|
||||
int r;
|
||||
int g;
|
||||
@@ -749,10 +749,10 @@ internal static class BcHelpers
|
||||
|
||||
if (indexBits2 == 0)
|
||||
{
|
||||
r = Interpolate(endpoints[partitionSet * 2][0], endpoints[(partitionSet * 2) + 1][0], weights, index);
|
||||
g = Interpolate(endpoints[partitionSet * 2][1], endpoints[(partitionSet * 2) + 1][1], weights, index);
|
||||
b = Interpolate(endpoints[partitionSet * 2][2], endpoints[(partitionSet * 2) + 1][2], weights, index);
|
||||
a = Interpolate(endpoints[partitionSet * 2][3], endpoints[(partitionSet * 2) + 1][3], weights, index);
|
||||
r = Interpolate(endpoints[partitionSet * 2, 0], endpoints[(partitionSet * 2) + 1, 0], weights, index);
|
||||
g = Interpolate(endpoints[partitionSet * 2, 1], endpoints[(partitionSet * 2) + 1, 1], weights, index);
|
||||
b = Interpolate(endpoints[partitionSet * 2, 2], endpoints[(partitionSet * 2) + 1, 2], weights, index);
|
||||
a = Interpolate(endpoints[partitionSet * 2, 3], endpoints[(partitionSet * 2) + 1, 3], weights, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -764,17 +764,17 @@ internal static class BcHelpers
|
||||
// and from the primary index bits otherwise.
|
||||
if (indexSelectionBit == 0)
|
||||
{
|
||||
r = Interpolate(endpoints[partitionSet * 2][0], endpoints[(partitionSet * 2) + 1][0], weights, index);
|
||||
g = Interpolate(endpoints[partitionSet * 2][1], endpoints[(partitionSet * 2) + 1][1], weights, index);
|
||||
b = Interpolate(endpoints[partitionSet * 2][2], endpoints[(partitionSet * 2) + 1][2], weights, index);
|
||||
a = Interpolate(endpoints[partitionSet * 2][3], endpoints[(partitionSet * 2) + 1][3], weights2, index2);
|
||||
r = Interpolate(endpoints[partitionSet * 2, 0], endpoints[(partitionSet * 2) + 1, 0], weights, index);
|
||||
g = Interpolate(endpoints[partitionSet * 2, 1], endpoints[(partitionSet * 2) + 1, 1], weights, index);
|
||||
b = Interpolate(endpoints[partitionSet * 2, 2], endpoints[(partitionSet * 2) + 1, 2], weights, index);
|
||||
a = Interpolate(endpoints[partitionSet * 2, 3], endpoints[(partitionSet * 2) + 1, 3], weights2, index2);
|
||||
}
|
||||
else
|
||||
{
|
||||
r = Interpolate(endpoints[partitionSet * 2][0], endpoints[(partitionSet * 2) + 1][0], weights2, index2);
|
||||
g = Interpolate(endpoints[partitionSet * 2][1], endpoints[(partitionSet * 2) + 1][1], weights2, index2);
|
||||
b = Interpolate(endpoints[partitionSet * 2][2], endpoints[(partitionSet * 2) + 1][2], weights2, index2);
|
||||
a = Interpolate(endpoints[partitionSet * 2][3], endpoints[(partitionSet * 2) + 1][3], weights, index);
|
||||
r = Interpolate(endpoints[partitionSet * 2, 0], endpoints[(partitionSet * 2) + 1, 0], weights2, index2);
|
||||
g = Interpolate(endpoints[partitionSet * 2, 1], endpoints[(partitionSet * 2) + 1, 1], weights2, index2);
|
||||
b = Interpolate(endpoints[partitionSet * 2, 2], endpoints[(partitionSet * 2) + 1, 2], weights2, index2);
|
||||
a = Interpolate(endpoints[partitionSet * 2, 3], endpoints[(partitionSet * 2) + 1, 3], weights, index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1056,15 +1056,4 @@ internal static class BcHelpers
|
||||
{
|
||||
(a, b) = (b, a);
|
||||
}
|
||||
|
||||
public static T[][] CreateRectangularArray<T>(int size1, int size2)
|
||||
{
|
||||
T[][] newArray = new T[size1][];
|
||||
for (int array1 = 0; array1 < size1; array1++)
|
||||
{
|
||||
newArray[array1] = new T[size2];
|
||||
}
|
||||
|
||||
return newArray;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Buffers.Binary;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Dxt
|
||||
{
|
||||
@@ -15,8 +16,7 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT1(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * sizeof(uint)];
|
||||
return DecompressDXT1(input, width, height, output);
|
||||
return DecompressDXT1<ColorBGRA32, byte>(input, width, height, out output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -29,45 +29,97 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT1(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output, width, height);
|
||||
|
||||
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="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT1<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return DecompressDXT1<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT1 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT1<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
return DecompressDXT1<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT1 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT1<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
|
||||
int offset = 0;
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
uint[] buffer = new uint[16];
|
||||
int[] colors = new int[4];
|
||||
Span<TOutputColor> buffer = stackalloc TOutputColor[16];
|
||||
Span<TOutputColor> colors = stackalloc TOutputColor[4];
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, offset += 8)
|
||||
{
|
||||
int q0 = input[offset + 0] | input[offset + 1] << 8;
|
||||
int q1 = input[offset + 2] | input[offset + 3] << 8;
|
||||
Rgb565(q0, out int r0, out int g0, out int b0);
|
||||
Rgb565(q1, out int r1, out int g1, out int b1);
|
||||
colors[0] = Color(r0, g0, b0, 255);
|
||||
colors[1] = Color(r1, g1, b1, 255);
|
||||
Rgb565(q0, out byte r0, out byte g0, out byte b0);
|
||||
Rgb565(q1, out byte r1, out byte g1, out byte b1);
|
||||
colors[0].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>(r0, g0, b0);
|
||||
colors[1].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>(r1, g1, b1);
|
||||
if (q0 > q1)
|
||||
{
|
||||
colors[2] = Color((r0 * 2 + r1) / 3, (g0 * 2 + g1) / 3, (b0 * 2 + b1) / 3, 255);
|
||||
colors[3] = Color((r0 + r1 * 2) / 3, (g0 + g1 * 2) / 3, (b0 + b1 * 2) / 3, 255);
|
||||
colors[2].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>((byte)((r0 * 2 + r1) / 3), (byte)((g0 * 2 + g1) / 3), (byte)((b0 * 2 + b1) / 3));
|
||||
colors[3].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>((byte)((r0 + r1 * 2) / 3), (byte)((g0 + g1 * 2) / 3), (byte)((b0 + b1 * 2) / 3));
|
||||
}
|
||||
else
|
||||
{
|
||||
colors[2] = Color((r0 + r1) / 2, (g0 + g1) / 2, (b0 + b1) / 2, 255);
|
||||
colors[2].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>((byte)((r0 + r1) / 2), (byte)((g0 + g1) / 2), (byte)((b0 + b1) / 2));
|
||||
colors[3].SetBlack<TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
|
||||
uint d = ToUInt32(input, offset + 4);
|
||||
for (int i = 0; i < 16; i++, d >>= 2)
|
||||
{
|
||||
buffer[i] = unchecked((uint)colors[d & 3]);
|
||||
buffer[i] = colors[unchecked((int)(d & 3))];
|
||||
}
|
||||
|
||||
int clen = (s < bcw - 1 ? 4 : clen_last) * 4;
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
ReadOnlySpan<byte> bufferSpan = MemoryMarshal.Cast<uint, byte>(new ReadOnlySpan<uint>(buffer));
|
||||
BlockCopy(bufferSpan, i * 4 * 4, output, (y * width + s * 4) * 4, clen);
|
||||
BlockCopy(buffer, i * 4, output, y * width + s * 4, clen);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,8 +137,7 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT3(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * sizeof(uint)];
|
||||
return DecompressDXT3(input, width, height, output);
|
||||
return DecompressDXT3<ColorBGRA32, byte>(input, width, height, out output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -99,15 +150,67 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT3(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output, width, height);
|
||||
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="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT3<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return DecompressDXT3<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT1 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT3<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
return DecompressDXT3<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT3 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT3<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
|
||||
int offset = 0;
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
uint[] buffer = new uint[16];
|
||||
int[] colors = new int[4];
|
||||
int[] alphas = new int[16];
|
||||
Span<TOutputColor> buffer = stackalloc TOutputColor[16];
|
||||
Span<ColorRGB<byte>> colors = stackalloc ColorRGB<byte>[4];
|
||||
Span<byte> alphas = stackalloc byte[16];
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, offset += 16)
|
||||
@@ -115,39 +218,39 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
int alpha = input[offset + i * 2] | input[offset + i * 2 + 1] << 8;
|
||||
alphas[i * 4 + 0] = (((alpha >> 0) & 0xF) * 0x11) << 24;
|
||||
alphas[i * 4 + 1] = (((alpha >> 4) & 0xF) * 0x11) << 24;
|
||||
alphas[i * 4 + 2] = (((alpha >> 8) & 0xF) * 0x11) << 24;
|
||||
alphas[i * 4 + 3] = (((alpha >> 12) & 0xF) * 0x11) << 24;
|
||||
alphas[i * 4 + 0] = (byte)(((alpha >> 0) & 0xF) * 0x11);
|
||||
alphas[i * 4 + 1] = (byte)(((alpha >> 4) & 0xF) * 0x11);
|
||||
alphas[i * 4 + 2] = (byte)(((alpha >> 8) & 0xF) * 0x11);
|
||||
alphas[i * 4 + 3] = (byte)(((alpha >> 12) & 0xF) * 0x11);
|
||||
}
|
||||
|
||||
int q0 = input[offset + 8] | input[offset + 9] << 8;
|
||||
int q1 = input[offset + 10] | input[offset + 11] << 8;
|
||||
Rgb565(q0, out int r0, out int g0, out int b0);
|
||||
Rgb565(q1, out int r1, out int g1, out int b1);
|
||||
colors[0] = Color(r0, g0, b0, 0);
|
||||
colors[1] = Color(r1, g1, b1, 0);
|
||||
Rgb565(q0, out byte r0, out byte g0, out byte b0);
|
||||
Rgb565(q1, out byte r1, out byte g1, out byte b1);
|
||||
colors[0] = new ColorRGB<byte>(r0, g0, b0);
|
||||
colors[1] = new ColorRGB<byte>(r1, g1, b1);
|
||||
if (q0 > q1)
|
||||
{
|
||||
colors[2] = Color((r0 * 2 + r1) / 3, (g0 * 2 + g1) / 3, (b0 * 2 + b1) / 3, 0);
|
||||
colors[3] = Color((r0 + r1 * 2) / 3, (g0 + g1 * 2) / 3, (b0 + b1 * 2) / 3, 0);
|
||||
colors[2] = new ColorRGB<byte>((byte)((r0 * 2 + r1) / 3), (byte)((g0 * 2 + g1) / 3), (byte)((b0 * 2 + b1) / 3));
|
||||
colors[3] = new ColorRGB<byte>((byte)((r0 + r1 * 2) / 3), (byte)((g0 + g1 * 2) / 3), (byte)((b0 + b1 * 2) / 3));
|
||||
}
|
||||
else
|
||||
{
|
||||
colors[2] = Color((r0 + r1) / 2, (g0 + g1) / 2, (b0 + b1) / 2, 0);
|
||||
colors[2] = new ColorRGB<byte>((byte)((r0 + r1) / 2), (byte)((g0 + g1) / 2), (byte)((b0 + b1) / 2));
|
||||
colors[3].SetBlack<ColorRGB<byte>, byte>();
|
||||
}
|
||||
|
||||
uint d = ToUInt32(input, offset + 12);
|
||||
for (int i = 0; i < 16; i++, d >>= 2)
|
||||
{
|
||||
buffer[i] = unchecked((uint)(colors[d & 3] | alphas[i]));
|
||||
buffer[i].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>(colors[unchecked((int)(d & 3))], alphas[i]);
|
||||
}
|
||||
|
||||
int clen = (s < bcw - 1 ? 4 : clen_last) * 4;
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
ReadOnlySpan<byte> bufferSpan = MemoryMarshal.Cast<uint, byte>(new ReadOnlySpan<uint>(buffer));
|
||||
BlockCopy(bufferSpan, i * 4 * 4, output, (y * width + s * 4) * 4, clen);
|
||||
BlockCopy(buffer, i * 4, output, y * width + s * 4, clen);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,8 +268,7 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT5(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * sizeof(uint)];
|
||||
return DecompressDXT5(input, width, height, output);
|
||||
return DecompressDXT5<ColorBGRA32, byte>(input, width, height, out output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -179,105 +281,150 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT5(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output, width, height);
|
||||
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="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT5<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return DecompressDXT5<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT1 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT5<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
return DecompressDXT5<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT5 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT5<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
|
||||
int offset = 0;
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
uint[] buffer = new uint[16];
|
||||
int[] colors = new int[4];
|
||||
int[] alphas = new int[8];
|
||||
Span<TOutputColor> buffer = stackalloc TOutputColor[16];
|
||||
Span<ColorRGB<byte>> colors = stackalloc ColorRGB<byte>[4];
|
||||
Span<byte> alphas = stackalloc byte[8];
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, offset += 16)
|
||||
{
|
||||
alphas[0] = input[offset + 0];
|
||||
alphas[1] = input[offset + 1];
|
||||
if (alphas[0] > alphas[1])
|
||||
byte a0 = input[offset + 0];
|
||||
byte a1 = input[offset + 1];
|
||||
alphas[0] = a0;
|
||||
alphas[1] = a1;
|
||||
if (a0 > a1)
|
||||
{
|
||||
alphas[2] = (alphas[0] * 6 + alphas[1]) / 7;
|
||||
alphas[3] = (alphas[0] * 5 + alphas[1] * 2) / 7;
|
||||
alphas[4] = (alphas[0] * 4 + alphas[1] * 3) / 7;
|
||||
alphas[5] = (alphas[0] * 3 + alphas[1] * 4) / 7;
|
||||
alphas[6] = (alphas[0] * 2 + alphas[1] * 5) / 7;
|
||||
alphas[7] = (alphas[0] + alphas[1] * 6) / 7;
|
||||
alphas[2] = (byte)((a0 * 6 + a1) / 7);
|
||||
alphas[3] = (byte)((a0 * 5 + a1 * 2) / 7);
|
||||
alphas[4] = (byte)((a0 * 4 + a1 * 3) / 7);
|
||||
alphas[5] = (byte)((a0 * 3 + a1 * 4) / 7);
|
||||
alphas[6] = (byte)((a0 * 2 + a1 * 5) / 7);
|
||||
alphas[7] = (byte)((a0 + a1 * 6) / 7);
|
||||
}
|
||||
else
|
||||
{
|
||||
alphas[2] = (alphas[0] * 4 + alphas[1]) / 5;
|
||||
alphas[3] = (alphas[0] * 3 + alphas[1] * 2) / 5;
|
||||
alphas[4] = (alphas[0] * 2 + alphas[1] * 3) / 5;
|
||||
alphas[5] = (alphas[0] + alphas[1] * 4) / 5;
|
||||
alphas[7] = 255;
|
||||
}
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
alphas[i] <<= 24;
|
||||
alphas[2] = (byte)((a0 * 4 + a1) / 5);
|
||||
alphas[3] = (byte)((a0 * 3 + a1 * 2) / 5);
|
||||
alphas[4] = (byte)((a0 * 2 + a1 * 3) / 5);
|
||||
alphas[5] = (byte)((a0 + a1 * 4) / 5);
|
||||
alphas[6] = byte.MinValue;
|
||||
alphas[7] = byte.MaxValue;
|
||||
}
|
||||
|
||||
int q0 = input[offset + 8] | input[offset + 9] << 8;
|
||||
int q1 = input[offset + 10] | input[offset + 11] << 8;
|
||||
Rgb565(q0, out int r0, out int g0, out int b0);
|
||||
Rgb565(q1, out int r1, out int g1, out int b1);
|
||||
colors[0] = Color(r0, g0, b0, 0);
|
||||
colors[1] = Color(r1, g1, b1, 0);
|
||||
Rgb565(q0, out byte r0, out byte g0, out byte b0);
|
||||
Rgb565(q1, out byte r1, out byte g1, out byte b1);
|
||||
colors[0] = new ColorRGB<byte>(r0, g0, b0);
|
||||
colors[1] = new ColorRGB<byte>(r1, g1, b1);
|
||||
if (q0 > q1)
|
||||
{
|
||||
colors[2] = Color((r0 * 2 + r1) / 3, (g0 * 2 + g1) / 3, (b0 * 2 + b1) / 3, 0);
|
||||
colors[3] = Color((r0 + r1 * 2) / 3, (g0 + g1 * 2) / 3, (b0 + b1 * 2) / 3, 0);
|
||||
colors[2] = new ColorRGB<byte>((byte)((r0 * 2 + r1) / 3), (byte)((g0 * 2 + g1) / 3), (byte)((b0 * 2 + b1) / 3));
|
||||
colors[3] = new ColorRGB<byte>((byte)((r0 + r1 * 2) / 3), (byte)((g0 + g1 * 2) / 3), (byte)((b0 + b1 * 2) / 3));
|
||||
}
|
||||
else
|
||||
{
|
||||
colors[2] = Color((r0 + r1) / 2, (g0 + g1) / 2, (b0 + b1) / 2, 0);
|
||||
colors[2] = new ColorRGB<byte>((byte)((r0 + r1) / 2), (byte)((g0 + g1) / 2), (byte)((b0 + b1) / 2));
|
||||
colors[3].SetBlack<ColorRGB<byte>, byte>();
|
||||
}
|
||||
|
||||
ulong da = ToUInt64(input, offset) >> 16;
|
||||
uint dc = ToUInt32(input, offset + 12);
|
||||
for (int i = 0; i < 16; i++, da >>= 3, dc >>= 2)
|
||||
{
|
||||
buffer[i] = unchecked((uint)(alphas[da & 7] | colors[dc & 3]));
|
||||
buffer[i].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>(colors[unchecked((int)(dc & 3))], alphas[unchecked((int)(da & 7))]);
|
||||
}
|
||||
|
||||
int clen = (s < bcw - 1 ? 4 : clen_last) * 4;
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
ReadOnlySpan<byte> bufferSpan = MemoryMarshal.Cast<uint, byte>(new ReadOnlySpan<uint>(buffer));
|
||||
BlockCopy(bufferSpan, i * 4 * 4, output, (y * width + s * 4) * 4, clen);
|
||||
BlockCopy(buffer, i * 4, output, y * width + s * 4, clen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static void Rgb565(int c, out int r, out int g, out int b)
|
||||
private static void Rgb565(int c, out byte r, out byte g, out byte b)
|
||||
{
|
||||
r = (c & 0xf800) >> 8;
|
||||
g = (c & 0x07e0) >> 3;
|
||||
b = (c & 0x001f) << 3;
|
||||
r |= r >> 5;
|
||||
g |= g >> 6;
|
||||
b |= b >> 5;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static int Color(int r, int g, int b, int a)
|
||||
{
|
||||
return r << 16 | g << 8 | b | a << 24;
|
||||
r = unchecked((byte)((c & 0xf800) >> 8));
|
||||
g = unchecked((byte)((c & 0x07e0) >> 3));
|
||||
b = unchecked((byte)((c & 0x001f) << 3));
|
||||
r |= (byte)(r >> 5);
|
||||
g |= (byte)(g >> 6);
|
||||
b |= (byte)(b >> 5);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Based on <see cref="Buffer.BlockCopy(Array, int, Array, int, int)"/>
|
||||
/// </summary>
|
||||
/// <param name="source">The source buffer.</param>
|
||||
/// <param name="sourceOffset">The zero-based byte offset into source.</param>
|
||||
/// <param name="sourceOffset">The zero-based offset into source.</param>
|
||||
/// <param name="destination">The destination buffer.</param>
|
||||
/// <param name="destinationOffset">The zero-based byte offset into destination.</param>
|
||||
/// <param name="count">The number of bytes to copy.</param>
|
||||
/// <param name="destinationOffset">The zero-based offset into destination.</param>
|
||||
/// <param name="count">The number of items to copy.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static void BlockCopy(ReadOnlySpan<byte> source, int sourceOffset, Span<byte> destination, int destinationOffset, int count)
|
||||
private static void BlockCopy<T>(ReadOnlySpan<T> source, int sourceOffset, Span<T> destination, int destinationOffset, int count)
|
||||
{
|
||||
source.Slice(sourceOffset, count).CopyTo(destination.Slice(destinationOffset));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Etc
|
||||
{
|
||||
public static class EtcDecoder
|
||||
@@ -10,7 +8,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return DecompressETC(input, width, height, output);
|
||||
}
|
||||
|
||||
public unsafe static int DecompressETC(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressETC(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
|
||||
if (input.Length < requiredLength)
|
||||
@@ -18,14 +16,6 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
throw new ArgumentException($"{nameof(input)} has length {input.Length} which is less than the required length {requiredLength}", nameof(input));
|
||||
}
|
||||
|
||||
fixed (byte* outputPtr = output)
|
||||
{
|
||||
return DecompressETC(input, width, height, outputPtr);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static int DecompressETC(ReadOnlySpan<byte> input, int width, int height, byte* output)
|
||||
{
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
@@ -37,16 +27,15 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
{
|
||||
DecodeEtc1Block(input.Slice(inputOffset, 8), buf);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
|
||||
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(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] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
}
|
||||
|
||||
outputPtr += width;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,7 +48,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return DecompressETC2(input, width, height, output);
|
||||
}
|
||||
|
||||
public unsafe static int DecompressETC2(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressETC2(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
|
||||
if (input.Length < requiredLength)
|
||||
@@ -67,14 +56,6 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
throw new ArgumentException($"{nameof(input)} has length {input.Length} which is less than the required length {requiredLength}", nameof(input));
|
||||
}
|
||||
|
||||
fixed (byte* outputPtr = output)
|
||||
{
|
||||
return DecompressETC2(input, width, height, outputPtr);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static int DecompressETC2(ReadOnlySpan<byte> input, int width, int height, byte* output)
|
||||
{
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
@@ -86,16 +67,15 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
{
|
||||
DecodeEtc2Block(input.Slice(inputOffset, 8), buf);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
|
||||
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(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] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
}
|
||||
|
||||
outputPtr += width;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,7 +88,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return DecompressETC2A1(input, width, height, output);
|
||||
}
|
||||
|
||||
public unsafe static int DecompressETC2A1(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressETC2A1(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
|
||||
if (input.Length < requiredLength)
|
||||
@@ -116,14 +96,6 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
throw new ArgumentException($"{nameof(input)} has length {input.Length} which is less than the required length {requiredLength}", nameof(input));
|
||||
}
|
||||
|
||||
fixed (byte* outputPtr = output)
|
||||
{
|
||||
return DecompressETC2A1(input, width, height, outputPtr);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static int DecompressETC2A1(ReadOnlySpan<byte> input, int width, int height, byte* output)
|
||||
{
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
@@ -135,16 +107,15 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
{
|
||||
DecodeEtc2a1Block(input.Slice(inputOffset, 8), buf);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
|
||||
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(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] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
}
|
||||
|
||||
outputPtr += width;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,7 +128,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return DecompressETC2A8(input, width, height, output);
|
||||
}
|
||||
|
||||
public unsafe static int DecompressETC2A8(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressETC2A8(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 16;
|
||||
if (input.Length < requiredLength)
|
||||
@@ -165,14 +136,6 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
throw new ArgumentException($"{nameof(input)} has length {input.Length} which is less than the required length {requiredLength}", nameof(input));
|
||||
}
|
||||
|
||||
fixed (byte* outputPtr = output)
|
||||
{
|
||||
return DecompressETC2A8(input, width, height, outputPtr);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static int DecompressETC2A8(ReadOnlySpan<byte> input, int width, int height, byte* output)
|
||||
{
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
@@ -185,16 +148,15 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
DecodeEtc2Block(input.Slice(inputOffset + 8, 8), buf);
|
||||
DecodeEtc2a8Block(input.Slice(inputOffset + 0, 8), buf);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
|
||||
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(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] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
}
|
||||
|
||||
outputPtr += width;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,7 +169,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return DecompressEACRUnsigned(input, width, height, output);
|
||||
}
|
||||
|
||||
public unsafe static int DecompressEACRUnsigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressEACRUnsigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
|
||||
if (input.Length < requiredLength)
|
||||
@@ -215,14 +177,6 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
throw new ArgumentException($"{nameof(input)} has length {input.Length} which is less than the required length {requiredLength}", nameof(input));
|
||||
}
|
||||
|
||||
fixed (byte* outputPtr = output)
|
||||
{
|
||||
return DecompressEACRUnsigned(input, width, height, outputPtr);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static int DecompressEACRUnsigned(ReadOnlySpan<byte> input, int width, int height, byte* output)
|
||||
{
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
@@ -238,16 +192,15 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
{
|
||||
DecodeEacUnsignedBlock(input.Slice(inputOffset, 8), buf, 2);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
|
||||
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(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] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
}
|
||||
|
||||
outputPtr += width;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,7 +213,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return DecompressEACRSigned(input, width, height, output);
|
||||
}
|
||||
|
||||
public unsafe static int DecompressEACRSigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressEACRSigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
|
||||
if (input.Length < requiredLength)
|
||||
@@ -268,14 +221,6 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
throw new ArgumentException($"{nameof(input)} has length {input.Length} which is less than the required length {requiredLength}", nameof(input));
|
||||
}
|
||||
|
||||
fixed (byte* outputPtr = output)
|
||||
{
|
||||
return DecompressEACRSigned(input, width, height, outputPtr);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static int DecompressEACRSigned(ReadOnlySpan<byte> input, int width, int height, byte* output)
|
||||
{
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
@@ -291,16 +236,15 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
{
|
||||
DecodeEacSignedBlock(input.Slice(inputOffset, 8), buf, 2);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
|
||||
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(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] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
}
|
||||
|
||||
outputPtr += width;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -313,7 +257,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return DecompressEACRGUnsigned(input, width, height, output);
|
||||
}
|
||||
|
||||
public unsafe static int DecompressEACRGUnsigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressEACRGUnsigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
@@ -358,7 +302,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
return DecompressEACRGSigned(input, width, height, output);
|
||||
}
|
||||
|
||||
public unsafe static int DecompressEACRGSigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
public static int DecompressEACRGSigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 16;
|
||||
if (input.Length < requiredLength)
|
||||
@@ -366,14 +310,6 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
throw new ArgumentException($"{nameof(input)} has length {input.Length} which is less than the required length {requiredLength}", nameof(input));
|
||||
}
|
||||
|
||||
fixed (byte* outputPtr = output)
|
||||
{
|
||||
return DecompressEACRGSigned(input, width, height, outputPtr);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static int DecompressEACRGSigned(ReadOnlySpan<byte> input, int width, int height, byte* output)
|
||||
{
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
@@ -390,16 +326,15 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
DecodeEacSignedBlock(input.Slice(inputOffset + 0, 8), buf, 2);
|
||||
DecodeEacSignedBlock(input.Slice(inputOffset + 8, 8), buf, 1);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
|
||||
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(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] = buf[j + 4 * i];
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
}
|
||||
|
||||
outputPtr += width;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -412,11 +347,11 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
private static void DecodeEtc1Block(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
{
|
||||
byte i3 = input[3];
|
||||
ReadOnlySpan<int> code = stackalloc int[2]
|
||||
{
|
||||
ReadOnlySpan<int> code =
|
||||
[
|
||||
(i3 >> 5) * 4,
|
||||
(i3 >> 2 & 7) * 4
|
||||
};
|
||||
];
|
||||
Span<byte> c = stackalloc byte[6];
|
||||
int ti = (i3 & 1) * 16;
|
||||
if ((i3 & 2) != 0)
|
||||
@@ -488,13 +423,13 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
}
|
||||
int ti = input[3] >> 1 & 6 | input[3] & 1;
|
||||
byte d = Etc2DistanceTable[ti];
|
||||
ReadOnlySpan<uint> color_set = stackalloc uint[]
|
||||
{
|
||||
ReadOnlySpan<uint> color_set =
|
||||
[
|
||||
ApplicateColorRaw(c, 0),
|
||||
ApplicateColor(c, 1, d),
|
||||
ApplicateColorRaw(c, 1),
|
||||
ApplicateColor(c, 1, -d)
|
||||
};
|
||||
];
|
||||
|
||||
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
|
||||
{
|
||||
@@ -529,13 +464,13 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
++di;
|
||||
}
|
||||
byte d = Etc2DistanceTable[di];
|
||||
ReadOnlySpan<uint> color_set = stackalloc uint[]
|
||||
{
|
||||
ReadOnlySpan<uint> color_set =
|
||||
[
|
||||
ApplicateColor(c, 0, d),
|
||||
ApplicateColor(c, 0, -d),
|
||||
ApplicateColor(c, 1, d),
|
||||
ApplicateColor(c, 1, -d)
|
||||
};
|
||||
];
|
||||
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
|
||||
{
|
||||
int index = k << 1 & 2 | j & 1;
|
||||
@@ -579,9 +514,11 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
else
|
||||
{
|
||||
// differential
|
||||
Span<int> code = stackalloc int[2];
|
||||
code[0] = (input[3] >> 5) * 4;
|
||||
code[1] = (input[3] >> 2 & 7) * 4;
|
||||
ReadOnlySpan<int> code =
|
||||
[
|
||||
(input[3] >> 5) * 4,
|
||||
(input[3] >> 2 & 7) * 4
|
||||
];
|
||||
int ti = (input[3] & 1) * 16;
|
||||
unchecked
|
||||
{
|
||||
@@ -612,11 +549,11 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
else
|
||||
{
|
||||
// individual
|
||||
ReadOnlySpan<int> code = stackalloc int[2]
|
||||
{
|
||||
ReadOnlySpan<int> code =
|
||||
[
|
||||
(input[3] >> 5) * 4,
|
||||
(input[3] >> 2 & 7) * 4
|
||||
};
|
||||
];
|
||||
int ti = (input[3] & 1) * 16;
|
||||
unchecked
|
||||
{
|
||||
@@ -675,13 +612,13 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
}
|
||||
int ti = input[3] >> 1 & 6 | input[3] & 1;
|
||||
byte d = Etc2DistanceTable[ti];
|
||||
ReadOnlySpan<uint> color_set = stackalloc uint[4]
|
||||
{
|
||||
ReadOnlySpan<uint> color_set =
|
||||
[
|
||||
ApplicateColorRaw(c, 0),
|
||||
ApplicateColor(c, 1, d),
|
||||
ApplicateColorRaw(c, 1),
|
||||
ApplicateColor(c, 1, -d)
|
||||
};
|
||||
];
|
||||
|
||||
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
|
||||
{
|
||||
@@ -717,13 +654,13 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
++di;
|
||||
}
|
||||
byte d = Etc2DistanceTable[di];
|
||||
ReadOnlySpan<uint> color_set = stackalloc uint[4]
|
||||
{
|
||||
ReadOnlySpan<uint> color_set =
|
||||
[
|
||||
ApplicateColor(c, 0, d),
|
||||
ApplicateColor(c, 0, -d),
|
||||
ApplicateColor(c, 1, d),
|
||||
ApplicateColor(c, 1, -d)
|
||||
};
|
||||
];
|
||||
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
|
||||
{
|
||||
int index = k << 1 & 2 | j & 1;
|
||||
@@ -768,11 +705,11 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
else
|
||||
{
|
||||
// differential (Etc1Block + mask + specific mod table)
|
||||
ReadOnlySpan<int> code = stackalloc int[2]
|
||||
{
|
||||
ReadOnlySpan<int> code =
|
||||
[
|
||||
(input[3] >> 5) * 4,
|
||||
(input[3] >> 2 & 7) * 4
|
||||
};
|
||||
];
|
||||
int ti = (input[3] & 1) * 16;
|
||||
unchecked
|
||||
{
|
||||
@@ -912,15 +849,15 @@ 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 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 =
|
||||
{
|
||||
[
|
||||
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 =
|
||||
{
|
||||
[
|
||||
2, 8, -2, -8,
|
||||
5, 17, -5, -17,
|
||||
9, 29, -9, -29,
|
||||
@@ -929,9 +866,9 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
24, 80, -24, -80,
|
||||
33, 106, -33, -106,
|
||||
47, 183, -47, -183,
|
||||
};
|
||||
];
|
||||
private static readonly int[] PunchthroughModifierTable =
|
||||
{
|
||||
[
|
||||
0, 8, 0, -8,
|
||||
0, 17, 0, -17,
|
||||
0, 29, 0, -29,
|
||||
@@ -940,10 +877,10 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
0, 80, 0, -80,
|
||||
0, 106, 0, -106,
|
||||
0, 183, 0, -183,
|
||||
};
|
||||
private static readonly byte[] Etc2DistanceTable = { 3, 6, 11, 16, 23, 32, 41, 64 };
|
||||
];
|
||||
private static readonly byte[] Etc2DistanceTable = [3, 6, 11, 16, 23, 32, 41, 64];
|
||||
private static readonly sbyte[] Etc2AlphaModTable =
|
||||
{
|
||||
[
|
||||
-3, -6, -9, -15, 2, 5, 8, 14,
|
||||
-3, -7, -10, -13, 2, 6, 9, 12,
|
||||
-2, -5, -8, -13, 1, 4, 7, 12,
|
||||
@@ -960,7 +897,7 @@ namespace AssetRipper.TextureDecoder.Etc
|
||||
-1, -2, -3, -10, 0, 1, 2, 9,
|
||||
-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 readonly uint[] PunchthroughMaskTable = [0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF];
|
||||
}
|
||||
}
|
||||
@@ -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.As<T, byte>(ref value));
|
||||
break;
|
||||
case sizeof(ushort):
|
||||
writer.Write(Unsafe.As<T, ushort>(ref value));
|
||||
break;
|
||||
case sizeof(uint):
|
||||
writer.Write(Unsafe.As<T, uint>(ref value));
|
||||
break;
|
||||
case sizeof(ulong):
|
||||
writer.Write(Unsafe.As<T, ulong>(ref value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteAttribute<T>(this BinaryWriter writer, ReadOnlySpan<byte> attributeName, T attributeValue) where T : IExrDataType
|
||||
{
|
||||
writer.Write(attributeName);
|
||||
writer.WriteNullByte();
|
||||
writer.Write(T.TypeName);
|
||||
writer.WriteNullByte();
|
||||
writer.Write(attributeValue.Size);
|
||||
attributeValue.Write(writer);
|
||||
}
|
||||
|
||||
public static void WriteAttribute<T>(this BinaryWriter writer, ReadOnlySpan<byte> attributeName, ReadOnlySpan<byte> attributeType, T attributeValue) where T : unmanaged, Enum
|
||||
{
|
||||
writer.Write(attributeName);
|
||||
writer.WriteNullByte();
|
||||
writer.Write(attributeType);
|
||||
writer.WriteNullByte();
|
||||
writer.Write(Unsafe.SizeOf<T>());
|
||||
writer.Write(attributeValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public static class ExrAttributes
|
||||
{
|
||||
public static ReadOnlySpan<byte> Channels => "channels"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> Compression => "compression"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> DataWindow => "dataWindow"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> DisplayWindow => "displayWindow"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> LineOrder => "lineOrder"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> PixelAspectRatio => "pixelAspectRatio"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> ScreenWindowCenter => "screenWindowCenter"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> ScreenWindowWidth => "screenWindowWidth"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> Tiles => "tiles"u8;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrBox2F(float XMin, float YMin, float XMax, float YMax) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "box2f"u8;
|
||||
|
||||
public int Size => sizeof(float) + sizeof(float) + sizeof(float) + sizeof(float);
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(XMin);
|
||||
writer.Write(YMin);
|
||||
writer.Write(XMax);
|
||||
writer.Write(YMax);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrBox2I(int XMin, int YMin, int XMax, int YMax) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "box2i"u8;
|
||||
|
||||
public int Size => sizeof(int) + sizeof(int) + sizeof(int) + sizeof(int);
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(XMin);
|
||||
writer.Write(YMin);
|
||||
writer.Write(XMax);
|
||||
writer.Write(YMax);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Text;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrChannel(string Name, ExrPixelType PixelType, bool PLinear, int XSampling, int YSampling)
|
||||
{
|
||||
public int Size
|
||||
{
|
||||
get
|
||||
{
|
||||
return Encoding.UTF8.GetByteCount(Name)
|
||||
+ sizeof(byte)//Null terminator
|
||||
+ sizeof(ExrPixelType)
|
||||
+ sizeof(bool)
|
||||
+ 3 * sizeof(byte)//Reserved bytes
|
||||
+ sizeof(int)
|
||||
+ sizeof(int);
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.WriteNullTerminatedString(Name);
|
||||
writer.Write(PixelType);
|
||||
writer.Write(PLinear);
|
||||
writer.WriteNullByte();
|
||||
writer.WriteNullByte();
|
||||
writer.WriteNullByte();
|
||||
writer.Write(XSampling);
|
||||
writer.Write(YSampling);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrChannelList(ExrChannel[] Channels) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "chlist"u8;
|
||||
|
||||
public int Size => Channels.Sum(c => c.Size);
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
foreach (ExrChannel channel in Channels)
|
||||
{
|
||||
channel.Write(writer);
|
||||
}
|
||||
writer.WriteNullByte();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public enum ExrCompression : byte
|
||||
{
|
||||
None = 0,
|
||||
RLE = 1,
|
||||
ZIPS = 2,
|
||||
ZIP = 3,
|
||||
PIZ = 4,
|
||||
PXR24 = 5,
|
||||
B44 = 6,
|
||||
B44A = 7,
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrDouble(double Value) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "double"u8;
|
||||
|
||||
public int Size => sizeof(double);
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(Value);
|
||||
}
|
||||
|
||||
public static implicit operator double(ExrDouble value) => value.Value;
|
||||
|
||||
public static implicit operator ExrDouble(double value) => new(value);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
public enum ExrImageFormat
|
||||
{
|
||||
ScanLines,
|
||||
Tiles,
|
||||
}
|
||||
public static class ExrImageFormatExtensions
|
||||
{
|
||||
public static byte ToVersionByte(this ExrImageFormat format) => format switch
|
||||
{
|
||||
ExrImageFormat.Tiles => 0x02,
|
||||
_ => 0x00,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrInt32(int Value) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "int"u8;
|
||||
|
||||
public int Size => sizeof(int);
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(Value);
|
||||
}
|
||||
|
||||
public static implicit operator int(ExrInt32 value) => value.Value;
|
||||
|
||||
public static implicit operator ExrInt32(int value) => new(value);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
[Flags]
|
||||
public enum ExrLevelMode : byte
|
||||
{
|
||||
OneLevel = 0,
|
||||
MipMapLevels = 1,
|
||||
RipMapLevels = 2,
|
||||
RoundDown = 0,
|
||||
RoundUp = 16,
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public enum ExrLineOrder : byte
|
||||
{
|
||||
IncreasingY = 0,
|
||||
DecreasingY = 1,
|
||||
RandomY = 2,
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public enum ExrPixelType : int
|
||||
{
|
||||
UInt = 0,
|
||||
Half = 1,
|
||||
Single = 2,
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrSingle(float Value) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "float"u8;
|
||||
|
||||
public int Size => sizeof(float);
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(Value);
|
||||
}
|
||||
|
||||
public static implicit operator float(ExrSingle value) => value.Value;
|
||||
|
||||
public static implicit operator ExrSingle(float value) => new(value);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Text;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrString(string Value) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "string"u8;
|
||||
|
||||
public int Size => sizeof(int) + Encoding.UTF8.GetByteCount(Value);
|
||||
|
||||
public void Write(BinaryWriter writer) => writer.WriteLengthPrefixedString(Value);
|
||||
|
||||
public static implicit operator string(ExrString value) => value.Value;
|
||||
|
||||
public static implicit operator ExrString(string value) => new(value);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrVector2(Vector2 Value) : IExrDataType
|
||||
{
|
||||
public ExrVector2(float x, float y) : this(new Vector2(x, y))
|
||||
{
|
||||
}
|
||||
|
||||
public int Size => sizeof(float) + sizeof(float);
|
||||
|
||||
public float X => Value.X;
|
||||
|
||||
public float Y => Value.Y;
|
||||
|
||||
public static ReadOnlySpan<byte> TypeName => "v2f"u8;
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(X);
|
||||
writer.Write(Y);
|
||||
}
|
||||
|
||||
public static implicit operator Vector2(ExrVector2 value) => value.Value;
|
||||
|
||||
public static implicit operator ExrVector2(Vector2 value) => new(value);
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public static class ExrWriter
|
||||
{
|
||||
public static ReadOnlySpan<byte> Magic => [0x76, 0x2f, 0x31, 0x01];
|
||||
|
||||
public static void Write<TColor, TChannelValue>(Stream stream, int width, int height, ReadOnlySpan<TColor> pixels)
|
||||
where TChannelValue : unmanaged
|
||||
where TColor : unmanaged, IColor<TChannelValue>
|
||||
{
|
||||
int ChannelCount = Color.GetChannelCount<TColor>();
|
||||
ExrPixelType PixelType = Unsafe.SizeOf<TChannelValue>() <= sizeof(ushort) ? ExrPixelType.Half : ExrPixelType.Single;
|
||||
int ChannelSize = PixelType is ExrPixelType.Half ? Unsafe.SizeOf<Half>() : sizeof(float);
|
||||
|
||||
using BinaryWriter writer = new BinaryWriter(stream);
|
||||
|
||||
//Magic bytes
|
||||
writer.Write(Magic);
|
||||
|
||||
//Version bytes
|
||||
writer.Write((byte)2);//OpenEXR version 2
|
||||
writer.Write(ExrImageFormat.ScanLines.ToVersionByte());
|
||||
writer.WriteNullByte();
|
||||
writer.WriteNullByte();
|
||||
|
||||
ExrChannel[] channels;
|
||||
{
|
||||
Span<ExrChannel> buffer = [default, default, default, default];
|
||||
//Stackalloc doesn't work for managed types.
|
||||
//This can be reworked when ref structs can be used in generics.
|
||||
|
||||
int i = 0;
|
||||
|
||||
//The order doesn't really matter here because the format sorts channels alphabetically
|
||||
//when reading reading scan lines, but this makes that more clear.
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
buffer[i] = new ExrChannel("A", PixelType, false, 1, 1);
|
||||
i++;
|
||||
}
|
||||
if (TColor.HasBlueChannel)
|
||||
{
|
||||
buffer[i] = new ExrChannel("B", PixelType, false, 1, 1);
|
||||
i++;
|
||||
}
|
||||
if (TColor.HasGreenChannel)
|
||||
{
|
||||
buffer[i] = new ExrChannel("G", PixelType, false, 1, 1);
|
||||
i++;
|
||||
}
|
||||
if (TColor.HasRedChannel)
|
||||
{
|
||||
buffer[i] = new ExrChannel("R", PixelType, false, 1, 1);
|
||||
i++;
|
||||
}
|
||||
|
||||
channels = buffer[..i].ToArray();
|
||||
}
|
||||
|
||||
//Attributes
|
||||
writer.WriteAttribute(ExrAttributes.Channels, new ExrChannelList(channels));
|
||||
writer.WriteAttribute(ExrAttributes.Compression, "compression"u8, ExrCompression.None);
|
||||
writer.WriteAttribute(ExrAttributes.DataWindow, new ExrBox2I(0, 0, width - 1, height - 1));
|
||||
writer.WriteAttribute(ExrAttributes.DisplayWindow, new ExrBox2I(0, 0, width - 1, height - 1));
|
||||
writer.WriteAttribute(ExrAttributes.LineOrder, "lineOrder"u8, ExrLineOrder.IncreasingY);
|
||||
writer.WriteAttribute(ExrAttributes.PixelAspectRatio, new ExrSingle(1f));
|
||||
writer.WriteAttribute(ExrAttributes.ScreenWindowCenter, new ExrVector2(0f, 0f));
|
||||
writer.WriteAttribute(ExrAttributes.ScreenWindowWidth, new ExrSingle(1f));
|
||||
writer.WriteNullByte();//End of header
|
||||
|
||||
//Scan line offset table
|
||||
long offset = writer.BaseStream.Position + height * sizeof(ulong);
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
writer.Write(offset);
|
||||
offset += sizeof(int) + ChannelCount * ChannelSize * width;
|
||||
}
|
||||
|
||||
//Scan lines
|
||||
if (PixelType is ExrPixelType.Half)
|
||||
{
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
writer.Write(i);
|
||||
writer.Write(ChannelCount * ChannelSize * width);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
Half alpha = NumericConversion.Convert<TChannelValue, Half>(pixels[i * width + j].A);
|
||||
writer.Write(alpha);
|
||||
}
|
||||
}
|
||||
if (TColor.HasBlueChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
float blue = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].B);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
float alpha = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].A);
|
||||
writer.Write((Half)ConvertSRGB(blue * alpha));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write((Half)ConvertSRGB(blue));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (TColor.HasGreenChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
float green = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].G);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
float alpha = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].A);
|
||||
writer.Write((Half)ConvertSRGB(green * alpha));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write((Half)ConvertSRGB(green));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (TColor.HasRedChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
float red = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].R);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
float alpha = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].A);
|
||||
writer.Write((Half)ConvertSRGB(red * alpha));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write((Half)ConvertSRGB(red));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
writer.Write(i);
|
||||
writer.Write(ChannelCount * ChannelSize * width);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
float alpha = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].A);
|
||||
writer.Write(alpha);
|
||||
}
|
||||
}
|
||||
if (TColor.HasBlueChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
float blue = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].B);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
float alpha = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].A);
|
||||
writer.Write(ConvertSRGB(blue * alpha));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(ConvertSRGB(blue));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (TColor.HasGreenChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
float green = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].G);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
float alpha = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].A);
|
||||
writer.Write(ConvertSRGB(green * alpha));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(ConvertSRGB(green));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (TColor.HasRedChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
float red = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].R);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
float alpha = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].A);
|
||||
writer.Write(ConvertSRGB(red * alpha));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(ConvertSRGB(red));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static float ConvertSRGB(float value)
|
||||
{
|
||||
//https://en.wikipedia.org/wiki/SRGB#From_sRGB_to_CIE_XYZ
|
||||
//https://en.wikipedia.org/wiki/Gamma_correction#Computer_displays
|
||||
if (value < 0.04045)
|
||||
{
|
||||
return value / 12.92f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return float.Pow((value + 0.055f) / 1.055f, 2.4f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public interface IExrDataType
|
||||
{
|
||||
static abstract ReadOnlySpan<byte> TypeName { get; }
|
||||
|
||||
int Size { get; }
|
||||
|
||||
void Write(BinaryWriter writer);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct TileDescription(int XSize, int YSize, ExrLevelMode Mode) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "tileDesc"u8;
|
||||
|
||||
public int Size => sizeof(int) + sizeof(int) + sizeof(ExrLevelMode);
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(XSize);
|
||||
writer.Write(YSize);
|
||||
writer.Write(Mode);
|
||||
}
|
||||
}
|
||||
@@ -2,3 +2,4 @@
|
||||
global using System.Diagnostics.CodeAnalysis;
|
||||
global using System.Numerics;
|
||||
global using System.Runtime.CompilerServices;
|
||||
global using System.Runtime.InteropServices;
|
||||
@@ -1,25 +1,11 @@
|
||||
//#define DISABLE_TWINDDLING_ROUTINE
|
||||
#define ASSUME_IMAGE_TILING
|
||||
namespace AssetRipper.TextureDecoder.Pvrtc;
|
||||
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
public static partial class PvrtcDecoder
|
||||
{
|
||||
public static partial class PvrtcDecoder
|
||||
/// <summary>
|
||||
/// 8 bytes (64 bits) per block
|
||||
/// </summary>
|
||||
private readonly record struct AmtcBlock(uint PackedData0, uint PackedData1)
|
||||
{
|
||||
/// <summary>
|
||||
/// 8 bytes
|
||||
/// </summary>
|
||||
private struct AmtcBlock
|
||||
{
|
||||
public AmtcBlock(uint v0, uint v1)
|
||||
{
|
||||
PackedData0 = v0;
|
||||
PackedData1 = v1;
|
||||
}
|
||||
|
||||
// Uses 64 bits pre block
|
||||
public readonly uint PackedData0;
|
||||
public readonly uint PackedData1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,16 @@
|
||||
//#define DISABLE_TWINDDLING_ROUTINE
|
||||
#define ASSUME_IMAGE_TILING
|
||||
namespace AssetRipper.TextureDecoder.Pvrtc;
|
||||
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
public static partial class PvrtcDecoder
|
||||
{
|
||||
public static partial class PvrtcDecoder
|
||||
/// <summary>
|
||||
/// Low precision colours extracted from the blocks
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 2 * 4 integers
|
||||
/// </remarks>
|
||||
[InlineArray(8)]
|
||||
private struct Colours5554
|
||||
{
|
||||
/// <summary>
|
||||
/// Low precision colours extracted from the blocks
|
||||
/// </summary>
|
||||
private unsafe struct Colours5554
|
||||
{
|
||||
public fixed int Reps[2 * 4];
|
||||
}
|
||||
private int _element0;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
//#define DISABLE_TWINDDLING_ROUTINE
|
||||
#define ASSUME_IMAGE_TILING
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
{
|
||||
public static partial class PvrtcDecoder
|
||||
@@ -85,16 +83,16 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
if (changed)
|
||||
{
|
||||
AmtcBlock block00 = blocks[(int)blocki00];
|
||||
Unpack5554Colour(block00, m_colors.GetIntSpanForColor(0));
|
||||
Unpack5554Colour(block00, m_colors[0]);
|
||||
UnpackModulations(block00, do2bitMode, modulationVals, modulationModes, 0, 0);
|
||||
AmtcBlock block01 = blocks[(int)blocki01];
|
||||
Unpack5554Colour(block01, m_colors.GetIntSpanForColor(1));
|
||||
Unpack5554Colour(block01, m_colors[1]);
|
||||
UnpackModulations(block01, do2bitMode, modulationVals, modulationModes, xBlockSize, 0);
|
||||
AmtcBlock block10 = blocks[(int)blocki10];
|
||||
Unpack5554Colour(block10, m_colors.GetIntSpanForColor(2));
|
||||
Unpack5554Colour(block10, m_colors[2]);
|
||||
UnpackModulations(block10, do2bitMode, modulationVals, modulationModes, 0, BlockYSize);
|
||||
AmtcBlock block11 = blocks[(int)blocki11];
|
||||
Unpack5554Colour(block11, m_colors.GetIntSpanForColor(3));
|
||||
Unpack5554Colour(block11, m_colors[3]);
|
||||
UnpackModulations(block11, do2bitMode, modulationVals, modulationModes, xBlockSize, BlockYSize);
|
||||
|
||||
pblocki00 = blocki00;
|
||||
@@ -106,8 +104,8 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
}
|
||||
|
||||
// decompress the pixel. First compute the interpolated A and B signals
|
||||
InterpolateColours(m_colors.GetIntSpanForColor(0), m_colors.GetIntSpanForColor(1), m_colors.GetIntSpanForColor(2), m_colors.GetIntSpanForColor(3), 0, do2bitMode, x, y, aSig);
|
||||
InterpolateColours(m_colors.GetIntSpanForColor(0), m_colors.GetIntSpanForColor(1), m_colors.GetIntSpanForColor(2), m_colors.GetIntSpanForColor(3), 1, do2bitMode, x, y, bSig);
|
||||
InterpolateColours(m_colors[0], m_colors[1], m_colors[2], m_colors[3], 0, do2bitMode, x, y, aSig);
|
||||
InterpolateColours(m_colors[0], m_colors[1], m_colors[2], m_colors[3], 1, do2bitMode, x, y, bSig);
|
||||
GetModulationValue(x, y, do2bitMode, modulationVals, modulationModes, out int mod, out bool doPT);
|
||||
|
||||
// compute the modulated color. Swap red and blue channel
|
||||
@@ -143,11 +141,11 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
if (!IsPowerOf2((uint)ySize))
|
||||
if (!BitOperations.IsPow2(ySize))
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
if (!IsPowerOf2((uint)xSize))
|
||||
if (!BitOperations.IsPow2(xSize))
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
@@ -398,12 +396,14 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
/// </summary>
|
||||
private static void Unpack5554Colour(AmtcBlock block, Span<int> abColors)
|
||||
{
|
||||
Span<uint> rawBits = stackalloc uint[2];
|
||||
// extract A and B
|
||||
// 15 bits (shifted up by one)
|
||||
rawBits[0] = block.PackedData1 & (0xFFFE);
|
||||
// 16 bits
|
||||
rawBits[1] = block.PackedData1 >> 16;
|
||||
ReadOnlySpan<uint> rawBits =
|
||||
[
|
||||
// extract A and B
|
||||
// 15 bits (shifted up by one)
|
||||
block.PackedData1 & (0xFFFE),
|
||||
// 16 bits
|
||||
block.PackedData1 >> 16,
|
||||
];
|
||||
|
||||
// step through both colours
|
||||
for (int i = 0; i < 2; i++)
|
||||
@@ -455,29 +455,6 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static Span<int> GetIntSpanForColor(this Span<Colours5554> colorSpan, int index)
|
||||
{
|
||||
return MemoryMarshal.Cast<Colours5554, int>(colorSpan.Slice(index, 1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that a number is an integer power of two, i.e. 1, 2, 4, 8, ... etc. Returns false for zero
|
||||
/// </summary>
|
||||
/// <param name="input">A number</param>
|
||||
/// <returns>True if the number is an integer power of two, else false</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static bool IsPowerOf2(uint input)
|
||||
{
|
||||
if (input == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint minus1 = input - 1;
|
||||
return ((input | minus1) == (input ^ minus1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Define an expression to either wrap or clamp large or small vals to the legal coordinate range
|
||||
/// </summary>
|
||||
@@ -510,7 +487,7 @@ namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
private const int BlockX2bpp = 8;
|
||||
private const int BlockX4bpp = 4;
|
||||
|
||||
private static readonly int[] m_repVals0 = new int[] { 0, 3, 5, 8 };
|
||||
private static readonly int[] m_repVals1 = new int[] { 0, 4, 4, 8 };
|
||||
private static readonly int[] m_repVals0 = [0, 3, 5, 8];
|
||||
private static readonly int[] m_repVals1 = [0, 4, 4, 8];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
namespace AssetRipper.TextureDecoder;
|
||||
|
||||
public readonly ref struct ReadOnlySpan2D<T>
|
||||
{
|
||||
public int Width { get; }
|
||||
public int Height { get; }
|
||||
public ReadOnlySpan<T> Data { get; }
|
||||
|
||||
public ReadOnlySpan2D(ReadOnlySpan<T> data, int width, int height)
|
||||
{
|
||||
Validate(data, width, height);
|
||||
Data = data;
|
||||
Width = width;
|
||||
Height = height;
|
||||
|
||||
static void Validate(ReadOnlySpan<T> data, int width, int height)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(width);
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(height);
|
||||
if (data.Length != width * height)
|
||||
{
|
||||
throw new ArgumentException("Data length does not match width and height.", nameof(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//public ReadOnlySpan<T> this[int y] => Data.Slice(y * Width, Width);
|
||||
|
||||
public T this[int x, int y] => Data[y * Width + x];
|
||||
|
||||
public static explicit operator ReadOnlySpan2D<T>(T[] array) => new(array, array.Length, 1);
|
||||
|
||||
public static explicit operator ReadOnlySpan2D<T>(ReadOnlySpan<T> span) => new(span, span.Length, 1);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
public readonly struct A : IChannel
|
||||
{
|
||||
static bool IChannel.IsAlpha => true;
|
||||
static T IChannel.GetBlack<T>() => NumericConversion.GetMaximumValueSafe<T>();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
public readonly struct B : IChannel
|
||||
{
|
||||
static bool IChannel.IsBlue => true;
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
public static class Channel
|
||||
{
|
||||
public static bool TryGetRed<TChannel, TValue>(in TValue field, out TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsRed)
|
||||
{
|
||||
value = TChannel.GetRed(field);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryGetGreen<TChannel, TValue>(in TValue field, out TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsGreen)
|
||||
{
|
||||
value = TChannel.GetGreen(field);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryGetBlue<TChannel, TValue>(in TValue field, out TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsBlue)
|
||||
{
|
||||
value = TChannel.GetBlue(field);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryGetAlpha<TChannel, TValue>(in TValue field, out TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsAlpha)
|
||||
{
|
||||
value = TChannel.GetAlpha(field);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetIfRed<TChannel, TValue>(ref TValue field, TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsRed)
|
||||
{
|
||||
TChannel.SetRed(ref field, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetIfGreen<TChannel, TValue>(ref TValue field, TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsGreen)
|
||||
{
|
||||
TChannel.SetGreen(ref field, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetIfBlue<TChannel, TValue>(ref TValue field, TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsBlue)
|
||||
{
|
||||
TChannel.SetBlue(ref field, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetIfAlpha<TChannel, TValue>(ref TValue field, TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsAlpha)
|
||||
{
|
||||
TChannel.SetAlpha(ref field, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
public readonly struct G : IChannel
|
||||
{
|
||||
static bool IChannel.IsGreen => true;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
public readonly struct Gray : IChannel
|
||||
{
|
||||
static bool IChannel.IsRed => true;
|
||||
static bool IChannel.IsGreen => true;
|
||||
static bool IChannel.IsBlue => true;
|
||||
static bool IChannel.FullyUtilized => false;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
public interface IChannel
|
||||
{
|
||||
static virtual bool IsRed => false;
|
||||
static virtual bool IsGreen => false;
|
||||
static virtual bool IsBlue => false;
|
||||
static virtual bool IsAlpha => false;
|
||||
static virtual bool FullyUtilized => true;
|
||||
|
||||
static virtual T GetBlack<T>() where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<T>();
|
||||
}
|
||||
|
||||
static virtual T GetWhite<T>() where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
return NumericConversion.GetMaximumValueSafe<T>();
|
||||
}
|
||||
|
||||
static virtual T GetRed<T>(T field) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
return field;
|
||||
}
|
||||
|
||||
static virtual T GetGreen<T>(T field) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
return field;
|
||||
}
|
||||
|
||||
static virtual T GetBlue<T>(T field) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
return field;
|
||||
}
|
||||
|
||||
static virtual T GetAlpha<T>(T field) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
return field;
|
||||
}
|
||||
|
||||
static virtual void SetRed<T>(ref T field, T value) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
field = value;
|
||||
}
|
||||
|
||||
static virtual void SetGreen<T>(ref T field, T value) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
field = value;
|
||||
}
|
||||
|
||||
static virtual void SetBlue<T>(ref T field, T value) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
field = value;
|
||||
}
|
||||
|
||||
static virtual void SetAlpha<T>(ref T field, T value) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
field = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
public readonly struct R : IChannel
|
||||
{
|
||||
static bool IChannel.IsRed => true;
|
||||
}
|
||||
@@ -1,7 +1,33 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb;
|
||||
|
||||
public static class Color
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal static void SetBlack<TThis, TThisChannel>(this ref TThis color)
|
||||
where TThisChannel : unmanaged
|
||||
where TThis : unmanaged, IColor<TThisChannel>
|
||||
{
|
||||
color.SetChannels(
|
||||
NumericConversion.GetMinimumValue<TThisChannel>(),
|
||||
NumericConversion.GetMinimumValue<TThisChannel>(),
|
||||
NumericConversion.GetMinimumValue<TThisChannel>(),
|
||||
NumericConversion.GetMaximumValue<TThisChannel>());
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal static void SetWhite<TThis, TThisChannel>(this ref TThis color)
|
||||
where TThisChannel : unmanaged
|
||||
where TThis : unmanaged, IColor<TThisChannel>
|
||||
{
|
||||
color.SetChannels(
|
||||
NumericConversion.GetMaximumValue<TThisChannel>(),
|
||||
NumericConversion.GetMaximumValue<TThisChannel>(),
|
||||
NumericConversion.GetMaximumValue<TThisChannel>(),
|
||||
NumericConversion.GetMaximumValue<TThisChannel>());
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
internal static void SetConvertedChannels<TThis, TThisChannel, TSourceChannel>(this ref TThis color, TSourceChannel r, TSourceChannel g, TSourceChannel b)
|
||||
where TThisChannel : unmanaged
|
||||
@@ -29,17 +55,30 @@ public static class Color
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static int GetChannelCount<T>() where T : IColorBase
|
||||
internal static void SetConvertedChannels<TThis, TThisChannel, TSourceChannel>(this ref TThis color, ColorRGB<TSourceChannel> rgb, TSourceChannel a)
|
||||
where TThisChannel : unmanaged
|
||||
where TSourceChannel : unmanaged, INumberBase<TSourceChannel>, IMinMaxValue<TSourceChannel>
|
||||
where TThis : unmanaged, IColor<TThisChannel>
|
||||
{
|
||||
color.SetChannels(
|
||||
NumericConversion.Convert<TSourceChannel, TThisChannel>(rgb.R),
|
||||
NumericConversion.Convert<TSourceChannel, TThisChannel>(rgb.G),
|
||||
NumericConversion.Convert<TSourceChannel, TThisChannel>(rgb.B),
|
||||
NumericConversion.Convert<TSourceChannel, TThisChannel>(a));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static int GetChannelCount<T>() where T : IColor
|
||||
{
|
||||
return (T.HasRedChannel ? 1 : 0) + (T.HasGreenChannel ? 1 : 0) + (T.HasBlueChannel ? 1 : 0) + (T.HasAlphaChannel ? 1 : 0);
|
||||
}
|
||||
|
||||
[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))
|
||||
{
|
||||
@@ -48,8 +87,8 @@ public static class 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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} }}";
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
public partial struct ColorA<T> : IColor<T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
public partial struct ColorA<T> : IColor<ColorA<T>, T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
public readonly T R
|
||||
{
|
||||
@@ -24,6 +24,11 @@ public partial struct ColorA<T> : IColor<T> where T : unmanaged, INumberBase<T>,
|
||||
|
||||
public T A { get; set; }
|
||||
|
||||
public ColorA(T a)
|
||||
{
|
||||
A = a;
|
||||
}
|
||||
|
||||
public readonly void GetChannels(out T r, out T g, out T b, out T a)
|
||||
{
|
||||
r = R;
|
||||
@@ -37,12 +42,15 @@ public partial struct ColorA<T> : IColor<T> where T : unmanaged, INumberBase<T>,
|
||||
A = a;
|
||||
}
|
||||
|
||||
static bool IColorBase.HasRedChannel => false;
|
||||
static bool IColorBase.HasGreenChannel => false;
|
||||
static bool IColorBase.HasBlueChannel => false;
|
||||
static bool IColorBase.HasAlphaChannel => true;
|
||||
static bool IColorBase.ChannelsAreFullyUtilized => true;
|
||||
static Type IColorBase.ChannelType => typeof(T);
|
||||
static bool IColor.HasRedChannel => false;
|
||||
static bool IColor.HasGreenChannel => false;
|
||||
static bool IColor.HasBlueChannel => false;
|
||||
static bool IColor.HasAlphaChannel => true;
|
||||
static bool IColor.ChannelsAreFullyUtilized => true;
|
||||
static Type IColor.ChannelType => typeof(T);
|
||||
|
||||
public static ColorA<T> Black => new(NumericConversion.GetMaximumValueSafe<T>());
|
||||
static ColorA<T> IColor<ColorA<T>, T>.White => throw new NotSupportedException();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@@ -4,12 +4,12 @@ namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public partial struct ColorARGB16 : IColor<byte>
|
||||
{
|
||||
static bool IColorBase.HasRedChannel => true;
|
||||
static bool IColorBase.HasGreenChannel => true;
|
||||
static bool IColorBase.HasBlueChannel => true;
|
||||
static bool IColorBase.HasAlphaChannel => true;
|
||||
static bool IColorBase.ChannelsAreFullyUtilized => false;
|
||||
static Type IColorBase.ChannelType => typeof(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 => false;
|
||||
static Type IColor.ChannelType => typeof(byte);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@@ -4,12 +4,12 @@ namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public partial struct ColorARGB32 : IColor<byte>
|
||||
{
|
||||
static bool IColorBase.HasRedChannel => true;
|
||||
static bool IColorBase.HasGreenChannel => true;
|
||||
static bool IColorBase.HasBlueChannel => true;
|
||||
static bool IColorBase.HasAlphaChannel => true;
|
||||
static bool IColorBase.ChannelsAreFullyUtilized => true;
|
||||
static Type IColorBase.ChannelType => typeof(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()
|
||||
{
|
||||
|
||||
@@ -4,12 +4,12 @@ namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public partial struct ColorBGRA32 : IColor<byte>
|
||||
{
|
||||
static bool IColorBase.HasRedChannel => true;
|
||||
static bool IColorBase.HasGreenChannel => true;
|
||||
static bool IColorBase.HasBlueChannel => true;
|
||||
static bool IColorBase.HasAlphaChannel => true;
|
||||
static bool IColorBase.ChannelsAreFullyUtilized => true;
|
||||
static Type IColorBase.ChannelType => typeof(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()
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
public partial struct ColorR<T> : IColor<T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
public partial struct ColorR<T> : IColor<ColorR<T>, T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
public T R { get; set; }
|
||||
|
||||
@@ -24,6 +24,11 @@ public partial struct ColorR<T> : IColor<T> where T : unmanaged, INumberBase<T>,
|
||||
set { }
|
||||
}
|
||||
|
||||
public ColorR(T r)
|
||||
{
|
||||
R = r;
|
||||
}
|
||||
|
||||
public readonly void GetChannels(out T r, out T g, out T b, out T a)
|
||||
{
|
||||
r = R;
|
||||
@@ -37,12 +42,15 @@ public partial struct ColorR<T> : IColor<T> where T : unmanaged, INumberBase<T>,
|
||||
R = r;
|
||||
}
|
||||
|
||||
static bool IColorBase.HasRedChannel => true;
|
||||
static bool IColorBase.HasGreenChannel => false;
|
||||
static bool IColorBase.HasBlueChannel => false;
|
||||
static bool IColorBase.HasAlphaChannel => false;
|
||||
static bool IColorBase.ChannelsAreFullyUtilized => true;
|
||||
static Type IColorBase.ChannelType => typeof(T);
|
||||
static bool IColor.HasRedChannel => true;
|
||||
static bool IColor.HasGreenChannel => false;
|
||||
static bool IColor.HasBlueChannel => false;
|
||||
static bool IColor.HasAlphaChannel => false;
|
||||
static bool IColor.ChannelsAreFullyUtilized => true;
|
||||
static Type IColor.ChannelType => typeof(T);
|
||||
|
||||
public static ColorR<T> Black => new(NumericConversion.GetMinimumValueSafe<T>());
|
||||
public static ColorR<T> White => new(NumericConversion.GetMaximumValueSafe<T>());
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
public partial struct ColorRG<T> : IColor<T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
public partial struct ColorRG<T> : IColor<ColorRG<T>, T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
public T R { get; set; }
|
||||
|
||||
@@ -20,6 +20,12 @@ public partial struct ColorRG<T> : IColor<T> where T : unmanaged, INumberBase<T>
|
||||
set { }
|
||||
}
|
||||
|
||||
public ColorRG(T r, T g)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
}
|
||||
|
||||
public readonly void GetChannels(out T r, out T g, out T b, out T a)
|
||||
{
|
||||
r = R;
|
||||
@@ -34,12 +40,15 @@ public partial struct ColorRG<T> : IColor<T> where T : unmanaged, INumberBase<T>
|
||||
G = g;
|
||||
}
|
||||
|
||||
static bool IColorBase.HasRedChannel => true;
|
||||
static bool IColorBase.HasGreenChannel => true;
|
||||
static bool IColorBase.HasBlueChannel => false;
|
||||
static bool IColorBase.HasAlphaChannel => false;
|
||||
static bool IColorBase.ChannelsAreFullyUtilized => true;
|
||||
static Type IColorBase.ChannelType => typeof(T);
|
||||
static bool IColor.HasRedChannel => true;
|
||||
static bool IColor.HasGreenChannel => true;
|
||||
static bool IColor.HasBlueChannel => false;
|
||||
static bool IColor.HasAlphaChannel => false;
|
||||
static bool IColor.ChannelsAreFullyUtilized => true;
|
||||
static Type IColor.ChannelType => typeof(T);
|
||||
|
||||
public static ColorRG<T> Black => new(NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMinimumValueSafe<T>());
|
||||
public static ColorRG<T> White => new(NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>());
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
public partial struct ColorRGB<T> : IColor<T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
public partial struct ColorRGB<T> : IColor<ColorRGB<T>, T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
public T R { get; set; }
|
||||
|
||||
@@ -16,6 +16,13 @@ public partial struct ColorRGB<T> : IColor<T> where T : unmanaged, INumberBase<T
|
||||
set { }
|
||||
}
|
||||
|
||||
public ColorRGB(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;
|
||||
@@ -31,12 +38,15 @@ public partial struct ColorRGB<T> : IColor<T> where T : unmanaged, INumberBase<T
|
||||
B = b;
|
||||
}
|
||||
|
||||
static bool IColorBase.HasRedChannel => true;
|
||||
static bool IColorBase.HasGreenChannel => true;
|
||||
static bool IColorBase.HasBlueChannel => true;
|
||||
static bool IColorBase.HasAlphaChannel => false;
|
||||
static bool IColorBase.ChannelsAreFullyUtilized => true;
|
||||
static Type IColorBase.ChannelType => typeof(T);
|
||||
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 ColorRGB<T> Black => new(NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMinimumValueSafe<T>(), NumericConversion.GetMinimumValueSafe<T>());
|
||||
public static ColorRGB<T> White => new(NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>(), NumericConversion.GetMaximumValueSafe<T>());
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@@ -4,12 +4,12 @@ namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public partial struct ColorRGB16 : IColor<byte>
|
||||
{
|
||||
static bool IColorBase.HasRedChannel => true;
|
||||
static bool IColorBase.HasGreenChannel => true;
|
||||
static bool IColorBase.HasBlueChannel => true;
|
||||
static bool IColorBase.HasAlphaChannel => false;
|
||||
static bool IColorBase.ChannelsAreFullyUtilized => false;
|
||||
static Type IColorBase.ChannelType => typeof(byte);
|
||||
static bool IColor.HasRedChannel => true;
|
||||
static bool IColor.HasGreenChannel => true;
|
||||
static bool IColor.HasBlueChannel => true;
|
||||
static bool IColor.HasAlphaChannel => false;
|
||||
static bool IColor.ChannelsAreFullyUtilized => false;
|
||||
static Type IColor.ChannelType => typeof(byte);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@@ -4,12 +4,12 @@ namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public partial struct ColorRGB9e5 : IColor<double>
|
||||
{
|
||||
static bool IColorBase.HasRedChannel => true;
|
||||
static bool IColorBase.HasGreenChannel => true;
|
||||
static bool IColorBase.HasBlueChannel => true;
|
||||
static bool IColorBase.HasAlphaChannel => false;
|
||||
static bool IColorBase.ChannelsAreFullyUtilized => false;
|
||||
static Type IColorBase.ChannelType => typeof(double);
|
||||
static bool IColor.HasRedChannel => true;
|
||||
static bool IColor.HasGreenChannel => true;
|
||||
static bool IColor.HasBlueChannel => true;
|
||||
static bool IColor.HasAlphaChannel => false;
|
||||
static bool IColor.ChannelsAreFullyUtilized => false;
|
||||
static Type IColor.ChannelType => typeof(double);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
Loaded 100 of 114 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user