Compare commits

...
48 Commits
Author SHA1 Message Date
Jeremy Pritts f2772dcb83 Bump version 2024-04-01 21:33:33 -07:00
Jeremy Pritts 1e363e355e Update NuGet references 2024-03-24 17:03:38 -07:00
Jeremy Pritts 8e82e702a7 OpenEXR writing 2024-03-24 17:00:42 -07:00
Jeremy Pritts 1957784de4 Support specifying channels 2024-03-24 16:59:44 -07:00
Jeremy Pritts 1bf5f70ead Rename Channel to ChannelValue 2024-03-24 16:54:23 -07:00
dependabot[bot] 2bb946b26f Bump BenchmarkDotNet from 0.13.11 to 0.13.12
Bumps [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) from 0.13.11 to 0.13.12.
- [Release notes](https://github.com/dotnet/BenchmarkDotNet/releases)
- [Commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.13.11...v0.13.12)

---
updated-dependencies:
- dependency-name: BenchmarkDotNet
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2024-01-07 21:18:06 -05:00
ds5678 eed528cc68 Bump version 2023-12-30 17:01:05 -05:00
ds5678 c6d3236770 Resolve #59 2023-12-30 16:42:13 -05:00
Jeremy Pritts 34d8310764 Add test for ASTC error 2023-12-14 00:18:06 -05:00
Jeremy Pritts e7b9db0dd6 Bump version 2023-12-12 02:25:17 -05:00
dependabot[bot] 1e7ef0c44c Bump NUnit.Analyzers from 3.9.0 to 3.10.0
Bumps [NUnit.Analyzers](https://github.com/nunit/nunit.analyzers) from 3.9.0 to 3.10.0.
- [Release notes](https://github.com/nunit/nunit.analyzers/releases)
- [Changelog](https://github.com/nunit/nunit.analyzers/blob/master/CHANGES.txt)
- [Commits](https://github.com/nunit/nunit.analyzers/compare/3.9.0...3.10.0)

---
updated-dependencies:
- dependency-name: NUnit.Analyzers
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-12-10 21:32:47 -05:00
dependabot[bot] 0f2a6fdec1 Bump BenchmarkDotNet from 0.13.10 to 0.13.11
Bumps [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) from 0.13.10 to 0.13.11.
- [Release notes](https://github.com/dotnet/BenchmarkDotNet/releases)
- [Commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.13.10...v0.13.11)

---
updated-dependencies:
- dependency-name: BenchmarkDotNet
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-12-10 21:32:38 -05:00
dependabot[bot] 058a14953f Bump NUnit from 4.0.0 to 4.0.1
Bumps [NUnit](https://github.com/nunit/nunit) from 4.0.0 to 4.0.1.
- [Release notes](https://github.com/nunit/nunit/releases)
- [Changelog](https://github.com/nunit/nunit/blob/master/CHANGES.md)
- [Commits](https://github.com/nunit/nunit/compare/v4.0.0...v4.0.1)

---
updated-dependencies:
- dependency-name: NUnit
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-12-10 21:32:30 -05:00
dependabot[bot] 94f1a9ab8e Bump actions/setup-dotnet from 3.2.0 to 4.0.0
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 3.2.0 to 4.0.0.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v3.2.0...v4.0.0)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-12-05 00:59:49 -05:00
Jeremy Pritts 5953a2baf7 Use Span in EtcDecoder
* Resolves #58
2023-11-28 23:58:15 -05:00
Jeremy Pritts 4b398e6c94 Use collection expressions 2023-11-28 23:40:37 -05:00
Jeremy Pritts a25a42dbd4 Add Etc correctness tests
* Resolves #57
2023-11-28 23:34:43 -05:00
Jeremy Pritts e9674d1c39 Use cached delegates for EtcTests 2023-11-28 22:59:21 -05:00
Jeremy Pritts 05eaeb81dc Refactor EtcTests 2023-11-28 22:44:43 -05:00
Jeremy Pritts 76a8b2f837 Update PvrtcTests to assess correctness 2023-11-28 22:30:15 -05:00
dependabot[bot] c113649b6e Bump NUnit from 3.14.0 to 4.0.0
Bumps [NUnit](https://github.com/nunit/nunit) from 3.14.0 to 4.0.0.
- [Release notes](https://github.com/nunit/nunit/releases)
- [Changelog](https://github.com/nunit/nunit/blob/master/CHANGES.md)
- [Commits](https://github.com/nunit/nunit/compare/v3.14.0...v4.0.0)

---
updated-dependencies:
- dependency-name: NUnit
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-11-26 21:30:43 -05:00
Jeremy Pritts 07002c77ee Static properties for black and white 2023-11-17 11:44:08 -05:00
Jeremy Pritts bed45ee60e Rename IColorBase to IColor 2023-11-17 11:01:39 -05:00
Jeremy Pritts d8918771a9 Change IColor filename 2023-11-17 10:47:33 -05:00
Jeremy Pritts 5dba4cdb19 Use collection expressions 2023-11-17 10:21:55 -05:00
Jeremy Pritts b1803e8a35 Greatly reduce Bc7 allocations 2023-11-17 09:47:56 -05:00
Jeremy Pritts 5ec29c6a82 Span2D ref structs 2023-11-17 09:46:10 -05:00
Jeremy Pritts c6a1240499 Bc improvements:
* Expose BlockSize as public constants
* Refactor Bc7 to use more constants
* Add a partial block test
2023-11-16 20:12:13 -05:00
Jeremy Pritts 12c6750a88 Make Astc use safe code 2023-11-16 19:58:47 -05:00
Jeremy Pritts 5dacd489d0 Use InlineArray for PvrtcDecoder 2023-11-16 19:18:20 -05:00
Jeremy Pritts 509a567aa5 Publish AOT 2023-11-16 18:55:03 -05:00
Jeremy Pritts 1f1dd55e85 .NET 8 2023-11-16 18:42:55 -05:00
dependabot[bot] 5e28554f00 Bump Microsoft.SourceLink.GitHub from 1.1.1 to 8.0.0
Bumps [Microsoft.SourceLink.GitHub](https://github.com/dotnet/sourcelink) from 1.1.1 to 8.0.0.
- [Release notes](https://github.com/dotnet/sourcelink/releases)
- [Commits](https://github.com/dotnet/sourcelink/commits)

---
updated-dependencies:
- dependency-name: Microsoft.SourceLink.GitHub
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-11-15 22:49:24 -05:00
dependabot[bot] d4cd1d5560 Bump Microsoft.NET.Test.Sdk from 17.7.2 to 17.8.0
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.7.2 to 17.8.0.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md)
- [Commits](https://github.com/microsoft/vstest/compare/v17.7.2...v17.8.0)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-11-08 22:53:08 -05:00
dependabot[bot] 9ae8296532 Bump NUnit from 3.13.3 to 3.14.0
Bumps [NUnit](https://github.com/nunit/nunit) from 3.13.3 to 3.14.0.
- [Release notes](https://github.com/nunit/nunit/releases)
- [Changelog](https://github.com/nunit/nunit/blob/master/CHANGES.md)
- [Commits](https://github.com/nunit/nunit/compare/v3.13.3...v3.14.0)

---
updated-dependencies:
- dependency-name: NUnit
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-11-05 21:31:55 -05:00
dependabot[bot] 637a53fc6f Bump BenchmarkDotNet from 0.13.9 to 0.13.10
Bumps [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) from 0.13.9 to 0.13.10.
- [Release notes](https://github.com/dotnet/BenchmarkDotNet/releases)
- [Commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.13.9...v0.13.10)

---
updated-dependencies:
- dependency-name: BenchmarkDotNet
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-11-01 21:57:58 -04:00
dependabot[bot] 53f177cae3 Bump NUnit.Analyzers from 3.8.0 to 3.9.0
Bumps [NUnit.Analyzers](https://github.com/nunit/nunit.analyzers) from 3.8.0 to 3.9.0.
- [Release notes](https://github.com/nunit/nunit.analyzers/releases)
- [Changelog](https://github.com/nunit/nunit.analyzers/blob/master/CHANGES.txt)
- [Commits](https://github.com/nunit/nunit.analyzers/compare/3.8.0...3.9.0)

---
updated-dependencies:
- dependency-name: NUnit.Analyzers
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-10-29 21:27:13 -04:00
Jeremy Pritts c57303ff6b Simplify NumericConversion for signed integers 2023-10-06 23:22:05 -04:00
Jeremy Pritts bd834212ad Support nint, nuint, and NFloat 2023-10-06 23:16:16 -04:00
Jeremy Pritts f0bdada27c global using InteropServices 2023-10-06 22:59:04 -04:00
Jeremy Pritts 4a386d57d7 Change rgb SetConvertedChannels extension to use ColorRGB explicitly 2023-10-06 14:35:23 -04:00
Jeremy Pritts d96d8e6c35 Generic color struct support for DXT3 and DXT5 2023-10-06 14:21:31 -04:00
Jeremy Pritts c16bec4431 Generic color struct constructors 2023-10-06 13:56:34 -04:00
Jeremy Pritts 548d5e8aec Generic DXT1 support 2023-10-06 13:09:17 -04:00
Jeremy Pritts 9e21272b2b Remove method implementation attributes in Yuy2Decoder 2023-10-06 13:03:03 -04:00
Jeremy Pritts df1ab3c1e4 Simplify Yuy2Decoder to make nongeneric use generic 2023-10-06 12:25:39 -04:00
Jeremy Pritts 7c30eb44ee Parameterize BcTests 2023-10-06 12:04:22 -04:00
dependabot[bot] e32f934043 Bump BenchmarkDotNet from 0.13.8 to 0.13.9
Bumps [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet) from 0.13.8 to 0.13.9.
- [Release notes](https://github.com/dotnet/BenchmarkDotNet/releases)
- [Commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.13.8...v0.13.9)

---
updated-dependencies:
- dependency-name: BenchmarkDotNet
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
2023-10-05 22:30:10 -04:00
114 changed files with 4919 additions and 2317 deletions

No files matched your search

+2 -2
View File
@@ -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>
@@ -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);
}
}
}
+182 -121
View File
@@ -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>
@@ -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>();
}
}
}
}
}
}
@@ -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 _));
}
}
+24 -29
View File
@@ -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)
+90 -97
View File
@@ -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));
}
}
}
+36 -12
View File
@@ -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;
}
}
+69 -99
View File
@@ -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 -2
View File
@@ -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 -2
View File
@@ -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 -2
View File
@@ -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;
}
}
+5 -2
View File
@@ -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;
}
}
+5 -2
View File
@@ -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;
}
}
+5 -2
View File
@@ -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;
}
}
+204 -236
View File
@@ -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];
}
+20 -5
View File
@@ -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
+29 -40
View File
@@ -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;
}
}
+239 -92
View File
@@ -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));
}
+74 -137
View File
@@ -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);
}
+225
View File
@@ -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;
}
+48 -9
View File
@@ -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;
}
}
+131
View File
@@ -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} }}";
}
}
+154
View File
@@ -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} }}";
}
}
+177
View File
@@ -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} }}";
}
}
+200
View File
@@ -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