mirror of
https://github.com/AssetRipper/AssetRipper.TextureDecoder.git
synced 2026-11-13 04:26:30 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61c3a861f7 | ||
|
|
437db55ae8 | ||
|
|
6ed0c07a83 | ||
|
|
54e3e20d0c | ||
|
|
1281377f2e | ||
|
|
7693de3ef9 | ||
|
|
4040a0a7a7 | ||
|
|
07d932c1a1 | ||
|
|
be63ebdb23 | ||
|
|
8458eb5866 | ||
|
|
19204e4dd2 | ||
|
|
8aa1840eb4 | ||
|
|
45267fc7fd | ||
|
|
3ad6884592 | ||
|
|
7c65daebaa | ||
|
|
068140150b | ||
|
|
5f2e7dc5c3 | ||
|
|
84eeba1e36 | ||
|
|
88c33f5dfa | ||
|
|
9555cbf25c | ||
|
|
cab5e7cfb8 | ||
|
|
2611088bd9 | ||
|
|
2256161eab | ||
|
|
d9cbcc48d8 | ||
|
|
113b793a6e | ||
|
|
fac3ca64df | ||
|
|
1485a246f1 | ||
|
|
81ecc464e5 | ||
|
|
d158e90448 | ||
|
|
7f3808b075 | ||
|
|
23c8e2c64a | ||
|
|
ea1c9c34ce | ||
|
|
d55e1e69a8 | ||
|
|
f6ea7a9269 | ||
|
|
07b0e7d407 | ||
|
|
56a70a2895 | ||
|
|
f967a521b0 | ||
|
|
4f775c4eff | ||
|
|
9a5da95897 | ||
|
|
4e037d8ddc | ||
|
|
5d2f86b18f | ||
|
|
d701ff24fc | ||
|
|
7be1d516f1 | ||
|
|
578a0633d1 | ||
|
|
94e4994027 | ||
|
|
74d081ca21 | ||
|
|
8e1792bfaf | ||
|
|
734f158f7f | ||
|
|
7aa9794941 | ||
|
|
9675ef0943 | ||
|
|
d4f7b94dea | ||
|
|
20d450ac1f | ||
|
|
2711b749db | ||
|
|
6465aa35c3 | ||
|
|
80d8c58d3a | ||
|
|
f8bee850bd | ||
|
|
eeb8866c41 | ||
|
|
129576d48e | ||
|
|
f28d991abe | ||
|
|
b1056490ec | ||
|
|
cab5cabc4e | ||
|
|
b0038a5184 | ||
|
|
6665fab8d9 |
No files matched your search
@@ -0,0 +1,18 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "nuget" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
# Workflow files stored in the
|
||||
# default location of `.github/workflows`
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
@@ -12,9 +12,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v1
|
||||
uses: actions/setup-dotnet@v3.0.1
|
||||
with:
|
||||
dotnet-version: 6.0.x
|
||||
- name: Restore dependencies
|
||||
|
||||
+4
-1
@@ -3,4 +3,7 @@ obj
|
||||
*.csproj.user
|
||||
.vs
|
||||
.idea
|
||||
.vscode
|
||||
.vscode
|
||||
launchSettings.json
|
||||
test.png
|
||||
textureformats/
|
||||
@@ -0,0 +1,25 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Benchmarks;
|
||||
|
||||
public class Argb32Decoding
|
||||
{
|
||||
private const int Width = 256;//ignoring smaller mips
|
||||
private const int Height = 256;//ignoring smaller mips
|
||||
private readonly byte[] inputData;
|
||||
private readonly byte[] outputData;
|
||||
|
||||
public Argb32Decoding()
|
||||
{
|
||||
inputData = File.ReadAllBytes(PathConstants.PathToTestFiles + "Rgb/" + "test.argb32");
|
||||
outputData = new byte[Width * Height * sizeof(uint)];
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int OriginalMethod() => RgbConverter.ARGB32ToBGRA32(inputData, Width, Height, outputData);
|
||||
|
||||
[Benchmark]
|
||||
public int GenericMethod() => RgbConverter.Convert<ColorARGB32, byte, ColorBGRA32, byte>(inputData, Width, Height, outputData);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AssetRipper.TextureDecoder\AssetRipper.TextureDecoder.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace AssetRipper.TextureDecoder.Benchmarks;
|
||||
|
||||
internal static class PathConstants
|
||||
{
|
||||
internal const string PathToRepositoryRoot = "../../../../../../../../";
|
||||
internal const string PathToTestFiles = PathToRepositoryRoot + "TestFiles/";
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using BenchmarkDotNet.Running;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Benchmarks;
|
||||
|
||||
internal class Program
|
||||
{
|
||||
static void Main()
|
||||
{
|
||||
BenchmarkRunner.Run(typeof(Program).Assembly);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Benchmarks;
|
||||
|
||||
public class Rgb9e5Decoding
|
||||
{
|
||||
private const int Width = 256;//ignoring smaller mips
|
||||
private const int Height = 256;//ignoring smaller mips
|
||||
private readonly byte[] inputData;
|
||||
private readonly byte[] outputData;
|
||||
|
||||
public Rgb9e5Decoding()
|
||||
{
|
||||
inputData = File.ReadAllBytes(PathConstants.PathToTestFiles + "Rgb/" + "test.rgb9e5float");
|
||||
outputData = new byte[Width * Height * sizeof(uint)];
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int OriginalMethod() => RgbConverter.RGB9e5FloatToBGRA32(inputData, Width, Height, outputData);
|
||||
|
||||
[Benchmark]
|
||||
public int GenericMethod() => RgbConverter.Convert<ColorRGB9e5, double, ColorBGRA32, byte>(inputData, Width, Height, outputData);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<PublishTrimmed>true</PublishTrimmed>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="StbImageWriteSharp" Version="1.16.7" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AssetRipper.TextureDecoder\AssetRipper.TextureDecoder.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,76 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using StbImageWriteSharp;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.ConsoleApp;
|
||||
|
||||
public struct DirectBitmap<TColor, TColorArg>
|
||||
where TColorArg : unmanaged
|
||||
where TColor : unmanaged, IColor<TColorArg>
|
||||
{
|
||||
public DirectBitmap(int width, int height)
|
||||
{
|
||||
Width = width;
|
||||
Height = height;
|
||||
Data = new byte[CalculateByteSize(width, height)];
|
||||
}
|
||||
|
||||
public DirectBitmap(int width, int height, byte[] data)
|
||||
{
|
||||
if (data.Length < CalculateByteSize(width, height))
|
||||
{
|
||||
throw new ArgumentException("Data does not have enough space.", nameof(data));
|
||||
}
|
||||
|
||||
Width = width;
|
||||
Height = height;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
public void FlipY()
|
||||
{
|
||||
Span<TColor> pixels = Pixels;
|
||||
for (int row = 0, irow = Height - 1; row < irow; row++, irow--)
|
||||
{
|
||||
Span<TColor> rowTop = pixels.Slice(row * Width, Width);
|
||||
Span<TColor> rowBottom = pixels.Slice(irow * Width, Width);
|
||||
for (int i = 0; i < Width; i++)
|
||||
{
|
||||
(rowTop[i], rowBottom[i]) = (rowBottom[i], rowTop[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveAsPng(string path)
|
||||
{
|
||||
using Stream stream = File.OpenWrite(path);
|
||||
ImageWriter writer = new();
|
||||
if (typeof(TColor) == typeof(ColorRGBA32))
|
||||
{
|
||||
writer.WritePng(Data, Width, Height, ColorComponents.RedGreenBlueAlpha, stream);
|
||||
}
|
||||
else if (typeof(TColor) == typeof(ColorRGB24))
|
||||
{
|
||||
writer.WritePng(Data, Width, Height, ColorComponents.RedGreenBlue, stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
RgbConverter.Convert<TColor, TColorArg, ColorRGBA32, byte>(Bits, Width, Height, out byte[] output);
|
||||
writer.WritePng(output, Width, Height, ColorComponents.RedGreenBlueAlpha, stream);
|
||||
}
|
||||
}
|
||||
|
||||
public int Height { get; }
|
||||
public int Width { get; }
|
||||
public int ByteSize => CalculateByteSize(Width, Height);
|
||||
public Span<byte> Bits => new Span<byte>(Data, 0, ByteSize);
|
||||
public Span<TColor> Pixels => MemoryMarshal.Cast<byte, TColor>(Bits);
|
||||
private byte[] Data { get; }
|
||||
|
||||
private static int CalculateByteSize(int width, int height)
|
||||
{
|
||||
return width * height * Unsafe.SizeOf<TColor>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
using AssetRipper.TextureDecoder.Astc;
|
||||
using AssetRipper.TextureDecoder.Atc;
|
||||
using AssetRipper.TextureDecoder.Bc;
|
||||
using AssetRipper.TextureDecoder.Dxt;
|
||||
using AssetRipper.TextureDecoder.Etc;
|
||||
using AssetRipper.TextureDecoder.Pvrtc;
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using AssetRipper.TextureDecoder.Yuy2;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.ConsoleApp;
|
||||
|
||||
internal static class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
if (args.Length < 4)
|
||||
{
|
||||
Console.WriteLine("Format: {path} {type} {width} {height} {args4} {args5}");
|
||||
return;
|
||||
}
|
||||
string path = args[0];
|
||||
string type = args[1].ToLowerInvariant();
|
||||
int width = int.Parse(args[2]);
|
||||
int height = int.Parse(args[3]);
|
||||
string args4 = args.GetArgument(4);
|
||||
string args5 = args.GetArgument(5);
|
||||
byte[] data = File.ReadAllBytes(path);
|
||||
|
||||
DirectBitmap<ColorBGRA32, byte> bitmap = new DirectBitmap<ColorBGRA32, byte>(width, height);
|
||||
switch (type)
|
||||
{
|
||||
case "astc":
|
||||
DecodeAstc(data, width, height, args4, args5, bitmap.Bits);
|
||||
break;
|
||||
case "atc":
|
||||
DecodeAtc(data, width, height, args4, bitmap.Bits);
|
||||
break;
|
||||
case "bc":
|
||||
DecodeBc(data, width, height, args4, args5, bitmap.Bits);
|
||||
break;
|
||||
case "dxt":
|
||||
DecodeDxt(data, width, height, args4, bitmap.Bits);
|
||||
break;
|
||||
case "etc":
|
||||
DecodeEtc(data, width, height, args4, bitmap.Bits);
|
||||
break;
|
||||
case "pvrtc":
|
||||
DecodePvrtc(data, width, height, args4, bitmap.Bits);
|
||||
break;
|
||||
case "rgb":
|
||||
DecodeRgb(data, width, height, args4, bitmap.Bits);
|
||||
break;
|
||||
case "yuy2":
|
||||
Yuy2Decoder.DecompressYUY2(data, width, height, bitmap.Bits);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException(type);
|
||||
}
|
||||
|
||||
string dirPath = Path.GetDirectoryName(path) ?? Environment.CurrentDirectory;
|
||||
string name = Path.GetFileNameWithoutExtension(path);
|
||||
string newPath = Path.Combine(dirPath, name + ".png");
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsPng(newPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException("Only supported on Windows");
|
||||
}
|
||||
|
||||
Console.WriteLine("Done!");
|
||||
}
|
||||
|
||||
private static string GetArgument(this string[] args, int index)
|
||||
{
|
||||
return index < args.Length ? args[index] : string.Empty;
|
||||
}
|
||||
|
||||
private static void DecodeAstc(ReadOnlySpan<byte> input, int width, int height, string blockXSizeString, string blockYSizeString, Span<byte> output)
|
||||
{
|
||||
Console.WriteLine("Arg at index 4 : blockXSize");
|
||||
Console.WriteLine("Arg at index 5 : blockYSize");
|
||||
int blockXSize = int.Parse(blockXSizeString);
|
||||
int blockYSize = int.Parse(blockYSizeString);
|
||||
AstcDecoder.DecodeASTC(input, width, height, blockXSize, blockYSize, output);
|
||||
}
|
||||
|
||||
private static void DecodeAtc(ReadOnlySpan<byte> input, int width, int height, string modeString, Span<byte> output)
|
||||
{
|
||||
Console.WriteLine("Arg at index 4 : mode");
|
||||
Console.WriteLine(" 0 - ATC RGB4");
|
||||
Console.WriteLine(" 1 - ATC RGBA8");
|
||||
int mode = int.Parse(modeString);
|
||||
switch (mode)
|
||||
{
|
||||
case 0:
|
||||
AtcDecoder.DecompressAtcRgb4(input, width, height, output);
|
||||
break;
|
||||
case 1:
|
||||
AtcDecoder.DecompressAtcRgba8(input, width, height, output);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(mode.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void DecodeBc(ReadOnlySpan<byte> input, int width, int height, string modeString, string isSignedString, Span<byte> output)
|
||||
{
|
||||
Console.WriteLine("Arg at index 4 : mode");
|
||||
Console.WriteLine(" 1 - BC1");
|
||||
Console.WriteLine(" 2 - BC2");
|
||||
Console.WriteLine(" 3 - BC3");
|
||||
Console.WriteLine(" 4 - BC4");
|
||||
Console.WriteLine(" 5 - BC5");
|
||||
Console.WriteLine(" 6 - BC6H");
|
||||
Console.WriteLine(" 7 - BC7");
|
||||
Console.WriteLine("Arg at index 5 : isSigned (BC6H only)");
|
||||
int mode = int.Parse(modeString);
|
||||
switch (mode)
|
||||
{
|
||||
case 1:
|
||||
BcDecoder.DecompressBC1(input, width, height, output);
|
||||
break;
|
||||
case 2:
|
||||
BcDecoder.DecompressBC2(input, width, height, output);
|
||||
break;
|
||||
case 3:
|
||||
BcDecoder.DecompressBC3(input, width, height, output);
|
||||
break;
|
||||
case 4:
|
||||
BcDecoder.DecompressBC4(input, width, height, output);
|
||||
break;
|
||||
case 5:
|
||||
BcDecoder.DecompressBC5(input, width, height, output);
|
||||
break;
|
||||
case 6:
|
||||
BcDecoder.DecompressBC6H(input, width, height, bool.Parse(isSignedString), output);
|
||||
break;
|
||||
case 7:
|
||||
BcDecoder.DecompressBC7(input, width, height, output);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(mode.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void DecodeDxt(ReadOnlySpan<byte> input, int width, int height, string modeString, Span<byte> output)
|
||||
{
|
||||
Console.WriteLine("Arg at index 4 : mode");
|
||||
Console.WriteLine(" 0 - DXT1");
|
||||
Console.WriteLine(" 1 - DXT3");
|
||||
Console.WriteLine(" 2 - DXT5");
|
||||
int mode = int.Parse(modeString);
|
||||
switch (mode)
|
||||
{
|
||||
case 0:
|
||||
DxtDecoder.DecompressDXT1(input, width, height, output);
|
||||
break;
|
||||
case 1:
|
||||
DxtDecoder.DecompressDXT3(input, width, height, output);
|
||||
break;
|
||||
case 2:
|
||||
DxtDecoder.DecompressDXT5(input, width, height, output);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(mode.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void DecodeEtc(ReadOnlySpan<byte> input, int width, int height, string modeString, Span<byte> output)
|
||||
{
|
||||
Console.WriteLine("Arg at index 4 : mode");
|
||||
Console.WriteLine(" 0 - ETC");
|
||||
Console.WriteLine(" 1 - ETC2");
|
||||
Console.WriteLine(" 2 - ETC2a1");
|
||||
Console.WriteLine(" 3 - ETC2a8");
|
||||
Console.WriteLine(" 4 - EAC R");
|
||||
Console.WriteLine(" 5 - EAC signed R");
|
||||
Console.WriteLine(" 6 - EAC RG");
|
||||
Console.WriteLine(" 7 - EAC signed RG");
|
||||
int mode = int.Parse(modeString);
|
||||
switch (mode)
|
||||
{
|
||||
case 0:
|
||||
EtcDecoder.DecompressETC(input, width, height, output);
|
||||
break;
|
||||
case 1:
|
||||
EtcDecoder.DecompressETC2(input, width, height, output);
|
||||
break;
|
||||
case 2:
|
||||
EtcDecoder.DecompressETC2A1(input, width, height, output);
|
||||
break;
|
||||
case 3:
|
||||
EtcDecoder.DecompressETC2A8(input, width, height, output);
|
||||
break;
|
||||
case 4:
|
||||
EtcDecoder.DecompressEACRUnsigned(input, width, height, output);
|
||||
break;
|
||||
case 5:
|
||||
EtcDecoder.DecompressEACRSigned(input, width, height, output);
|
||||
break;
|
||||
case 6:
|
||||
EtcDecoder.DecompressEACRGUnsigned(input, width, height, output);
|
||||
break;
|
||||
case 7:
|
||||
EtcDecoder.DecompressEACRGSigned(input, width, height, output);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(mode.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void DecodePvrtc(ReadOnlySpan<byte> input, int width, int height, string do2bitModeString, Span<byte> output)
|
||||
{
|
||||
Console.WriteLine("Arg at index 4 : 2bitMode");
|
||||
bool do2bit = bool.Parse(do2bitModeString);
|
||||
PvrtcDecoder.DecompressPVRTC(input, width, height, do2bit, output);
|
||||
}
|
||||
|
||||
private static void DecodeRgb(ReadOnlySpan<byte> input, int width, int height, string modeString, Span<byte> output)
|
||||
{
|
||||
Console.WriteLine("Arg at index 4 : mode");
|
||||
Console.WriteLine(" 0 - Alpha8");
|
||||
Console.WriteLine(" 1 - Argb4444");
|
||||
Console.WriteLine(" 2 - Rgb24");
|
||||
Console.WriteLine(" 3 - Rgba32");
|
||||
Console.WriteLine(" 4 - Argb32");
|
||||
Console.WriteLine(" 5 - Rbg16");
|
||||
Console.WriteLine(" 6 - R16");
|
||||
Console.WriteLine(" 7 - Rgba4444");
|
||||
Console.WriteLine(" 8 - Bgra32");
|
||||
Console.WriteLine(" 9 - Rg16");
|
||||
Console.WriteLine(" 10 - R8");
|
||||
Console.WriteLine(" 11 - RHalf");
|
||||
Console.WriteLine(" 12 - RGHalf");
|
||||
Console.WriteLine(" 13 - RGBAHalf");
|
||||
Console.WriteLine(" 14 - RFloat");
|
||||
Console.WriteLine(" 15 - RGFloat");
|
||||
Console.WriteLine(" 16 - RGBAFloat");
|
||||
Console.WriteLine(" 17 - RGB9e5Float");
|
||||
Console.WriteLine(" 18 - RG32");
|
||||
Console.WriteLine(" 19 - RGB48");
|
||||
Console.WriteLine(" 20 - RGBA64");
|
||||
int mode = int.Parse(modeString);
|
||||
switch (mode)
|
||||
{
|
||||
case 0:
|
||||
RgbConverter.A8ToBGRA32(input, width, height, output);
|
||||
break;
|
||||
case 1:
|
||||
RgbConverter.ARGB16ToBGRA32(input, width, height, output);
|
||||
break;
|
||||
case 2:
|
||||
RgbConverter.RGB24ToBGRA32(input, width, height, output);
|
||||
break;
|
||||
case 3:
|
||||
RgbConverter.RGBA32ToBGRA32(input, width, height, output);
|
||||
break;
|
||||
case 4:
|
||||
RgbConverter.ARGB32ToBGRA32(input, width, height, output);
|
||||
break;
|
||||
case 5:
|
||||
RgbConverter.RGB16ToBGRA32(input, width, height, output);
|
||||
break;
|
||||
case 6:
|
||||
RgbConverter.R16ToBGRA32(input, width, height, output);
|
||||
break;
|
||||
case 7:
|
||||
RgbConverter.RGBA16ToBGRA32(input, width, height, output);
|
||||
break;
|
||||
case 8:
|
||||
input.CopyTo(output);
|
||||
break;
|
||||
case 9:
|
||||
RgbConverter.RG16ToBGRA32(input, width, height, output);
|
||||
break;
|
||||
case 10:
|
||||
RgbConverter.R8ToBGRA32(input, width, height, output);
|
||||
break;
|
||||
case 11:
|
||||
RgbConverter.RHalfToBGRA32(input, width, height, output);
|
||||
break;
|
||||
case 12:
|
||||
RgbConverter.RGHalfToBGRA32(input, width, height, output);
|
||||
break;
|
||||
case 13:
|
||||
RgbConverter.RGBAHalfToBGRA32(input, width, height, output);
|
||||
break;
|
||||
case 14:
|
||||
RgbConverter.RFloatToBGRA32(input, width, height, output);
|
||||
break;
|
||||
case 15:
|
||||
RgbConverter.RGFloatToBGRA32(input, width, height, output);
|
||||
break;
|
||||
case 16:
|
||||
RgbConverter.RGBAFloatToBGRA32(input, width, height, output);
|
||||
break;
|
||||
case 17:
|
||||
RgbConverter.RGB9e5FloatToBGRA32(input, width, height, output);
|
||||
break;
|
||||
case 18:
|
||||
RgbConverter.RG32ToBGRA32(input, width, height, output);
|
||||
break;
|
||||
case 19:
|
||||
RgbConverter.RGB48ToBGRA32(input, width, height, output);
|
||||
break;
|
||||
case 20:
|
||||
RgbConverter.RGBA64ToBGRA32(input, width, height, output);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(mode.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AssetRipper.TextureDecoder\AssetRipper.TextureDecoder.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace AssetRipper.TextureDecoder.TestGenerator
|
||||
{
|
||||
internal struct ColorRandomValues
|
||||
{
|
||||
public string DefaultR;
|
||||
public string DefaultG;
|
||||
public string DefaultB;
|
||||
public string DefaultA;
|
||||
public string SetValue;
|
||||
|
||||
public ColorRandomValues(string defaultR, string defaultG, string defaultB, string defaultA, string setValue)
|
||||
{
|
||||
DefaultR = defaultR;
|
||||
DefaultG = defaultG;
|
||||
DefaultB = defaultB;
|
||||
DefaultA = defaultA;
|
||||
SetValue = setValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace AssetRipper.TextureDecoder.TestGenerator
|
||||
{
|
||||
internal struct GenerationData
|
||||
{
|
||||
public Type ColorType;
|
||||
public Type ChannelType;
|
||||
public int ColorSize;
|
||||
|
||||
public GenerationData(Type colorType, Type channelType, int colorSize)
|
||||
{
|
||||
ColorType = colorType;
|
||||
ChannelType = channelType;
|
||||
ColorSize = colorSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.CodeDom.Compiler;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.TestGenerator
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
private const string OutputNamespace = "AssetRipper.TextureDecoder.Tests.Formats";
|
||||
private const string OutputFolder = "../../../../AssetRipper.TextureDecoder.Tests/Formats/";
|
||||
|
||||
private static readonly char[] channels = new char[] { 'R', 'G', 'B', 'A' };
|
||||
|
||||
private static readonly List<GenerationData> dataList = new()
|
||||
{
|
||||
new GenerationData(typeof(ColorA8), typeof(byte), 1),
|
||||
new GenerationData(typeof(ColorARGB16), typeof(byte), 2),
|
||||
new GenerationData(typeof(ColorARGB32), typeof(byte), 4),
|
||||
new GenerationData(typeof(ColorR16), typeof(ushort), 2),
|
||||
new GenerationData(typeof(ColorR8), typeof(byte), 1),
|
||||
new GenerationData(typeof(ColorRG16), typeof(byte), 2),
|
||||
new GenerationData(typeof(ColorRG32), typeof(ushort), 4),
|
||||
new GenerationData(typeof(ColorRGB16), typeof(byte), 2),
|
||||
new GenerationData(typeof(ColorRGB24), typeof(byte), 3),
|
||||
new GenerationData(typeof(ColorRGB48), typeof(ushort), 6),
|
||||
new GenerationData(typeof(ColorRGB9e5), typeof(double), 4),
|
||||
new GenerationData(typeof(ColorRGBA16), typeof(byte), 2),
|
||||
new GenerationData(typeof(ColorRGBA32), typeof(byte), 4),
|
||||
new GenerationData(typeof(ColorRGBA64), typeof(ushort), 8),
|
||||
new GenerationData(typeof(ColorRGBAHalf), typeof(Half), 8),
|
||||
new GenerationData(typeof(ColorRGBASingle), typeof(float), 16),
|
||||
new GenerationData(typeof(ColorRGBHalf), typeof(Half), 6),
|
||||
new GenerationData(typeof(ColorRGBSingle), typeof(float), 12),
|
||||
new GenerationData(typeof(ColorRGHalf), typeof(Half), 4),
|
||||
new GenerationData(typeof(ColorRGSingle), typeof(float), 8),
|
||||
new GenerationData(typeof(ColorRHalf), typeof(Half), 2),
|
||||
new GenerationData(typeof(ColorRSingle), typeof(float), 4),
|
||||
};
|
||||
|
||||
private static readonly Dictionary<Type, ColorRandomValues> randomValueDictionary = new()
|
||||
{
|
||||
{ typeof(byte), new ColorRandomValues("0b11010101", "0b01110010", "0b10001111", "0b11000111", "0b01001110") },
|
||||
{ typeof(ushort), new ColorRandomValues("44000", "21354", "60080", "347", "33871") },
|
||||
{ typeof(Half), new ColorRandomValues("(Half)0.447f", "(Half)0.224f", "(Half)0.95f", "(Half)0.897f", "(Half)0.333f") },
|
||||
{ typeof(float), new ColorRandomValues("0.447f", "0.224f", "0.95f", "0.897f", "0.333f") },
|
||||
{ typeof(double), new ColorRandomValues("0.447", "0.224", "0.95", "0.897", "0.333") },
|
||||
};
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Directory.CreateDirectory(OutputFolder);
|
||||
|
||||
foreach (GenerationData data in dataList)
|
||||
{
|
||||
using MemoryStream memoryStream = new();
|
||||
using StreamWriter streamWriter = new StreamWriter(memoryStream);
|
||||
using IndentedTextWriter textWriter = new IndentedTextWriter(streamWriter, "\t");
|
||||
textWriter.NewLine = "\r\n";
|
||||
|
||||
textWriter.WriteSourceGenerationDisclaimer();
|
||||
textWriter.WriteNamespaceUsings(data);
|
||||
textWriter.WriteFileScopedNamespace();
|
||||
textWriter.WriteClassDefinitionOpen(data);
|
||||
textWriter.Indent += 1;
|
||||
|
||||
textWriter.WriteCorrectSizeTest(data);
|
||||
textWriter.WriteLine();
|
||||
textWriter.WritePropertySymmetryTests(data);
|
||||
textWriter.WriteIndependenceTests(data);
|
||||
textWriter.WriteGetMethodMatchesProperties();
|
||||
textWriter.WriteLine();
|
||||
textWriter.WriteMethodSymmetryTest();
|
||||
textWriter.WriteLine();
|
||||
textWriter.WriteMakeRandomColor(data);
|
||||
|
||||
textWriter.Indent -= 1;
|
||||
textWriter.WriteClassDefinitionClose();
|
||||
|
||||
textWriter.Flush();
|
||||
streamWriter.Flush();
|
||||
|
||||
string outputFilePath = $"{OutputFolder}{data.ColorType.Name}Tests.g.cs";
|
||||
File.WriteAllBytes(outputFilePath, memoryStream.ToArray());
|
||||
Console.WriteLine(outputFilePath);
|
||||
}
|
||||
|
||||
Console.WriteLine("Done!");
|
||||
}
|
||||
|
||||
private static void WriteNamespaceUsings(this IndentedTextWriter textWriter, GenerationData data)
|
||||
{
|
||||
textWriter.WriteLine($"using {data.ColorType.Namespace};");
|
||||
textWriter.WriteLine($"using {typeof(System.Runtime.CompilerServices.Unsafe).Namespace};");
|
||||
textWriter.WriteLine();//An empty line after the usings
|
||||
}
|
||||
|
||||
private static void WriteFileScopedNamespace(this IndentedTextWriter textWriter)
|
||||
{
|
||||
textWriter.WriteLine($"namespace {OutputNamespace};");
|
||||
textWriter.WriteLine();//An empty line after the namespace declaration
|
||||
}
|
||||
|
||||
private static void WriteSourceGenerationDisclaimer(this IndentedTextWriter textWriter)
|
||||
{
|
||||
textWriter.WriteLine($"//This code is source generated. Do not edit manually.");
|
||||
textWriter.WriteLine();
|
||||
}
|
||||
|
||||
private static void WriteClassDefinitionOpen(this IndentedTextWriter textWriter, GenerationData data)
|
||||
{
|
||||
textWriter.WriteLine($"public partial class {data.ColorType.Name}Tests");
|
||||
textWriter.WriteLine("{");
|
||||
}
|
||||
|
||||
private static void WriteClassDefinitionClose(this IndentedTextWriter textWriter)
|
||||
{
|
||||
textWriter.WriteLine("}");
|
||||
}
|
||||
|
||||
private static void WriteCorrectSizeTest(this IndentedTextWriter textWriter, GenerationData data)
|
||||
{
|
||||
textWriter.WriteLine("[Test]");
|
||||
textWriter.WriteLine("public void CorrectSizeTest()");
|
||||
textWriter.WriteLine("{");
|
||||
textWriter.Indent += 1;
|
||||
textWriter.WriteLine($"Assert.That(Unsafe.SizeOf<{data.ColorType.Name}>(), Is.EqualTo({data.ColorSize}));");
|
||||
textWriter.Indent -= 1;
|
||||
textWriter.WriteLine("}");
|
||||
}
|
||||
|
||||
private static void WriteMakeRandomColor(this IndentedTextWriter textWriter, GenerationData data)
|
||||
{
|
||||
textWriter.WriteLine($"public static {data.ColorType.Name} MakeRandomColor()");
|
||||
textWriter.WriteLine("{");
|
||||
textWriter.Indent += 1;
|
||||
|
||||
textWriter.WriteLine("return new()");
|
||||
textWriter.WriteLine("{");
|
||||
textWriter.Indent += 1;
|
||||
|
||||
ColorRandomValues randomValues = randomValueDictionary[data.ChannelType];
|
||||
textWriter.WriteLine($"R = {randomValues.DefaultR},");
|
||||
textWriter.WriteLine($"G = {randomValues.DefaultG},");
|
||||
textWriter.WriteLine($"B = {randomValues.DefaultB},");
|
||||
textWriter.WriteLine($"A = {randomValues.DefaultA},");
|
||||
|
||||
textWriter.Indent -= 1;
|
||||
textWriter.WriteLine("};");
|
||||
|
||||
textWriter.Indent -= 1;
|
||||
textWriter.WriteLine("}");
|
||||
}
|
||||
|
||||
private static void WritePropertySymmetryTests(this IndentedTextWriter textWriter, GenerationData data)
|
||||
{
|
||||
foreach(char channel in channels)
|
||||
{
|
||||
textWriter.WritePropertySymmetryTest(data, channel);
|
||||
textWriter.WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
private static void WritePropertySymmetryTest(this IndentedTextWriter textWriter, GenerationData data, char channel)
|
||||
{
|
||||
textWriter.WriteLine("[Test]");
|
||||
textWriter.WriteLine($"public void PropertyIsSymmetric_{channel}()");
|
||||
textWriter.WriteLine("{");
|
||||
textWriter.Indent += 1;
|
||||
textWriter.WriteLine("var color = MakeRandomColor();");
|
||||
char variableName = char.ToLowerInvariant(channel);
|
||||
textWriter.WriteLine($"var {variableName} = color.{channel};");
|
||||
textWriter.WriteLine($"color.{channel} = {variableName};");
|
||||
textWriter.WriteLine($"Assert.That(color.{channel}, Is.EqualTo({variableName}));");
|
||||
textWriter.Indent -= 1;
|
||||
textWriter.WriteLine("}");
|
||||
}
|
||||
|
||||
private static void WriteIndependenceTests(this IndentedTextWriter textWriter, GenerationData data)
|
||||
{
|
||||
foreach (char channel in channels)
|
||||
{
|
||||
textWriter.WriteIndependenceTest(data, channel);
|
||||
textWriter.WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteIndependenceTest(this IndentedTextWriter textWriter, GenerationData data, char channel)
|
||||
{
|
||||
textWriter.WriteLine("[Test]");
|
||||
textWriter.WriteLine($"public void ChannelsAreIndependent_{channel}()");
|
||||
textWriter.WriteLine("{");
|
||||
textWriter.Indent += 1;
|
||||
|
||||
textWriter.WriteLine("var color = MakeRandomColor();");
|
||||
foreach(char otherChannel in channels.Where(c => c != channel))
|
||||
{
|
||||
textWriter.WriteLine($"var {char.ToLowerInvariant(otherChannel)} = color.{otherChannel};");
|
||||
}
|
||||
textWriter.WriteLine($"color.{channel} = {randomValueDictionary[data.ChannelType].SetValue};");
|
||||
|
||||
textWriter.WriteLine("Assert.Multiple(() =>");
|
||||
textWriter.WriteLine("{");
|
||||
textWriter.Indent += 1;
|
||||
|
||||
foreach (char otherChannel in channels.Where(c => c != channel))
|
||||
{
|
||||
textWriter.WriteLine($"Assert.That(color.{otherChannel}, Is.EqualTo({char.ToLowerInvariant(otherChannel)}));");
|
||||
}
|
||||
|
||||
textWriter.Indent -= 1;
|
||||
textWriter.WriteLine("});");
|
||||
|
||||
textWriter.Indent -= 1;
|
||||
textWriter.WriteLine("}");
|
||||
}
|
||||
|
||||
private static void WriteGetMethodMatchesProperties(this IndentedTextWriter textWriter)
|
||||
{
|
||||
textWriter.WriteLine("[Test]");
|
||||
textWriter.WriteLine($"public void GetMethodMatchesProperties()");
|
||||
textWriter.WriteLine("{");
|
||||
textWriter.Indent += 1;
|
||||
|
||||
textWriter.WriteLine("var color = MakeRandomColor();");
|
||||
textWriter.WriteLine("color.GetChannels(out var r, out var g, out var b, out var a);");
|
||||
textWriter.WriteLine("Assert.Multiple(() =>");
|
||||
textWriter.WriteLine("{");
|
||||
textWriter.Indent += 1;
|
||||
|
||||
foreach (char channel in channels)
|
||||
{
|
||||
textWriter.WriteLine($"Assert.That(color.{channel}, Is.EqualTo({char.ToLowerInvariant(channel)}));");
|
||||
}
|
||||
|
||||
textWriter.Indent -= 1;
|
||||
textWriter.WriteLine("});");
|
||||
|
||||
textWriter.Indent -= 1;
|
||||
textWriter.WriteLine("}");
|
||||
}
|
||||
|
||||
private static void WriteMethodSymmetryTest(this IndentedTextWriter textWriter)
|
||||
{
|
||||
textWriter.WriteLine("[Test]");
|
||||
textWriter.WriteLine($"public void MethodsAreSymmetric()");
|
||||
textWriter.WriteLine("{");
|
||||
textWriter.Indent += 1;
|
||||
|
||||
textWriter.WriteLine("var color = MakeRandomColor();");
|
||||
textWriter.WriteLine("color.GetChannels(out var r, out var g, out var b, out var a);");
|
||||
textWriter.WriteLine("color.SetChannels(r, g, b, a);");
|
||||
textWriter.WriteLine("Assert.Multiple(() =>");
|
||||
textWriter.WriteLine("{");
|
||||
textWriter.Indent += 1;
|
||||
|
||||
foreach (char channel in channels)
|
||||
{
|
||||
textWriter.WriteLine($"Assert.That(color.{channel}, Is.EqualTo({char.ToLowerInvariant(channel)}));");
|
||||
}
|
||||
|
||||
textWriter.Indent -= 1;
|
||||
textWriter.WriteLine("});");
|
||||
|
||||
textWriter.Indent -= 1;
|
||||
textWriter.WriteLine("}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
|
||||
<PackageReference Include="NUnit.Analyzers" Version="3.3.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AssetRipper.TextureDecoder\AssetRipper.TextureDecoder.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
{
|
||||
public sealed class AtcTests
|
||||
{
|
||||
[Test]
|
||||
public void DecompressAtcRgb4Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.AtcTestFilesFolder + "test.atc_rgb4");
|
||||
int bytesRead = Atc.AtcDecoder.DecompressAtcRgb4(data, 256, 256, out _);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressAtcRgba8Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.AtcTestFilesFolder + "test.atc_rgba8");
|
||||
int bytesRead = Atc.AtcDecoder.DecompressAtcRgba8(data, 256, 256, out _);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
using AssetRipper.TextureDecoder.Bc;
|
||||
using AssetRipper.TextureDecoder.Dxt;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
{
|
||||
public sealed class BcTests
|
||||
{
|
||||
private const double MaxMeanDeviationBc6h = 0.5;
|
||||
private const double MaxMeanDeviationBc7 = 0.1;//Bc7 is more accurate than Bc6h
|
||||
private const double MaxStandardDeviationBc6h = 0.8;
|
||||
private const double MaxStandardDeviationBc7 = 0.8;
|
||||
|
||||
private static byte[] originalBgra32LogoData = Array.Empty<byte>();
|
||||
|
||||
[SetUp]
|
||||
public void InitializeOriginalData()
|
||||
{
|
||||
originalBgra32LogoData = File.ReadAllBytes(PathConstants.BcTestFilesFolder + "test.bgra32");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressDXT1Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.DxtTestFilesFolder + "test.dxt1");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4 }) //mipmaps 2 and 1 cause a buffer overrun for the output
|
||||
{
|
||||
int bytesRead = BcDecoder.DecompressBC1(data.Slice(totalBytesRead), size, size, out byte[] bcDecodedData);
|
||||
DxtDecoder.DecompressDXT1(data.Slice(totalBytesRead), size, size, out byte[] dxtDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
AssertAlmostEqual(dxtDecodedData, bcDecodedData);
|
||||
}
|
||||
Assert.That(totalBytesRead + 16, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressDXT3Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.DxtTestFilesFolder + "test.dxt3");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4 }) //mipmaps 2 and 1 cause a buffer overrun for the output
|
||||
{
|
||||
int bytesRead = BcDecoder.DecompressBC2(data.Slice(totalBytesRead), size, size, out byte[] bcDecodedData);
|
||||
DxtDecoder.DecompressDXT3(data.Slice(totalBytesRead), size, size, out byte[] dxtDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
AssertAlmostEqual(dxtDecodedData, bcDecodedData);
|
||||
}
|
||||
Assert.That(totalBytesRead + 32, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressDXT5Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.DxtTestFilesFolder + "test.dxt5");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4 }) //mipmaps 2 and 1 cause a buffer overrun for the output
|
||||
{
|
||||
int bytesRead = BcDecoder.DecompressBC3(data.Slice(totalBytesRead), size, size, out byte[] bcDecodedData);
|
||||
DxtDecoder.DecompressDXT5(data.Slice(totalBytesRead), size, size, out byte[] dxtDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
AssertAlmostEqual(dxtDecodedData, bcDecodedData);
|
||||
}
|
||||
Assert.That(totalBytesRead + 32, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressBC4Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.BcTestFilesFolder + "test.bc4");
|
||||
int bytesRead = BcDecoder.DecompressBC4(data, 512, 512, out _);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressBC5Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.BcTestFilesFolder + "test.bc5");
|
||||
int bytesRead = BcDecoder.DecompressBC5(data, 512, 512, out _);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressBC6HFastTest()
|
||||
{
|
||||
AssertCorrectBC6HDecompression(PathConstants.BcTestFilesFolder + "test.bc6h_fast", 512, 512, false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressBC6HNormalTest()
|
||||
{
|
||||
AssertCorrectBC6HDecompression(PathConstants.BcTestFilesFolder + "test.bc6h_normal", 512, 512, false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressBC6HBestTest()
|
||||
{
|
||||
AssertCorrectBC6HDecompression(PathConstants.BcTestFilesFolder + "test.bc6h_best", 512, 512, false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressBC7FastTest()
|
||||
{
|
||||
AssertCorrectBC7Decompression(PathConstants.BcTestFilesFolder + "test.bc7_fast", 512, 512);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressBC7NormalTest()
|
||||
{
|
||||
AssertCorrectBC7Decompression(PathConstants.BcTestFilesFolder + "test.bc7_normal", 512, 512);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressBC7BestTest()
|
||||
{
|
||||
AssertCorrectBC7Decompression(PathConstants.BcTestFilesFolder + "test.bc7_best", 512, 512);
|
||||
}
|
||||
|
||||
private static void AssertCorrectBC6HDecompression(string path, int width, int height, bool isSigned)
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(path);
|
||||
int bytesRead = BcDecoder.DecompressBC6H(data, width, height, isSigned, out byte[] decodedData);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
AssertMinimalDeviation(decodedData, originalBgra32LogoData, MaxMeanDeviationBc6h, MaxStandardDeviationBc6h);
|
||||
}
|
||||
|
||||
private static void AssertCorrectBC7Decompression(string path, int width, int height)
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(path);
|
||||
int bytesRead = BcDecoder.DecompressBC7(data, width, height, out byte[] decodedData);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
AssertMinimalDeviation(decodedData, originalBgra32LogoData, MaxMeanDeviationBc7, MaxStandardDeviationBc7);
|
||||
}
|
||||
|
||||
private static void AssertAlmostEqual(byte[] array1, byte[] array2, int maxDifference = 2)
|
||||
{
|
||||
if (array1.Length != array2.Length)
|
||||
{
|
||||
Assert.Fail($"Differing array lengths\nArray 1 length {array1.Length}\nArray 2 length {array2.Length}");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < array1.Length; i++)
|
||||
{
|
||||
int difference = Math.Abs(array1[i] - array2[i]);
|
||||
if (difference > maxDifference)
|
||||
{
|
||||
Assert.Fail($"Differing values at index {i}\nArray 1: {array1[i]}\nArray 2: {array2[i]}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AssertMinimalDeviation(byte[] decoded, byte[] original, double maxMeanDeviation, double maxStandardDeviation)
|
||||
{
|
||||
if (decoded.Length != original.Length)
|
||||
{
|
||||
Assert.Fail($"Differing array lengths\nDecoded length {decoded.Length}\nOriginal length {original.Length}");
|
||||
return;
|
||||
}
|
||||
|
||||
long differenceSum = 0;
|
||||
|
||||
for (int i = 0; i < decoded.Length; i++)
|
||||
{
|
||||
differenceSum += decoded[i] - original[i];
|
||||
}
|
||||
|
||||
double mean = differenceSum / (double)decoded.Length;
|
||||
|
||||
double sumOfSquaredDeviations = 0;
|
||||
|
||||
for (int i = 0; i < decoded.Length; i++)
|
||||
{
|
||||
double deviation = decoded[i] - original[i] - mean;
|
||||
sumOfSquaredDeviations += deviation * deviation;
|
||||
}
|
||||
|
||||
double standardDeviation = Math.Sqrt(sumOfSquaredDeviations / (decoded.Length - 1));
|
||||
//Not sure if Bessel's correction is needed here, but it doesn't hurt, especially since length is around 1 million in the current use.
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(mean, Is.LessThan(maxMeanDeviation), "Mean too far positive");
|
||||
Assert.That(mean, Is.GreaterThan(-maxMeanDeviation), "Mean too far negative");
|
||||
Assert.That(standardDeviation, Is.LessThan(maxStandardDeviation), "Standard deviation too large");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
{
|
||||
public sealed class DxtTests
|
||||
{
|
||||
[Test]
|
||||
public void DecompressDXT1Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.DxtTestFilesFolder + "test.dxt1");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Dxt.DxtDecoder.DecompressDXT1(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressDXT3Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.DxtTestFilesFolder + "test.dxt3");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Dxt.DxtDecoder.DecompressDXT3(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressDXT5Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.DxtTestFilesFolder + "test.dxt5");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Dxt.DxtDecoder.DecompressDXT5(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
{
|
||||
public sealed class EtcTests
|
||||
{
|
||||
[Test]
|
||||
public void DecompressETCTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.EtcTestFilesFolder + "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));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressETC2Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.EtcTestFilesFolder + "test.etc2");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Etc.EtcDecoder.DecompressETC2(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DecompressETC2A1Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.EtcTestFilesFolder + "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(PathConstants.EtcTestFilesFolder + "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(PathConstants.EtcTestFilesFolder + "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(PathConstants.EtcTestFilesFolder + "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(PathConstants.EtcTestFilesFolder + "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(PathConstants.EtcTestFilesFolder + "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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorA8Tests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorA8>(), Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorA8 MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = 0b11010101,
|
||||
G = 0b01110010,
|
||||
B = 0b10001111,
|
||||
A = 0b11000111,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorARGB16Tests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorARGB16>(), Is.EqualTo(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorARGB16 MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = 0b11010101,
|
||||
G = 0b01110010,
|
||||
B = 0b10001111,
|
||||
A = 0b11000111,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorARGB32Tests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorARGB32>(), Is.EqualTo(4));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorARGB32 MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = 0b11010101,
|
||||
G = 0b01110010,
|
||||
B = 0b10001111,
|
||||
A = 0b11000111,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorR16Tests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorR16>(), Is.EqualTo(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = 33871;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = 33871;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = 33871;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = 33871;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorR16 MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = 44000,
|
||||
G = 21354,
|
||||
B = 60080,
|
||||
A = 347,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorR8Tests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorR8>(), Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorR8 MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = 0b11010101,
|
||||
G = 0b01110010,
|
||||
B = 0b10001111,
|
||||
A = 0b11000111,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorRG16Tests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorRG16>(), Is.EqualTo(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorRG16 MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = 0b11010101,
|
||||
G = 0b01110010,
|
||||
B = 0b10001111,
|
||||
A = 0b11000111,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorRG32Tests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorRG32>(), Is.EqualTo(4));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = 33871;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = 33871;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = 33871;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = 33871;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorRG32 MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = 44000,
|
||||
G = 21354,
|
||||
B = 60080,
|
||||
A = 347,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorRGB16Tests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorRGB16>(), Is.EqualTo(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorRGB16 MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = 0b11010101,
|
||||
G = 0b01110010,
|
||||
B = 0b10001111,
|
||||
A = 0b11000111,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorRGB24Tests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorRGB24>(), Is.EqualTo(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorRGB24 MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = 0b11010101,
|
||||
G = 0b01110010,
|
||||
B = 0b10001111,
|
||||
A = 0b11000111,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorRGB48Tests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorRGB48>(), Is.EqualTo(6));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = 33871;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = 33871;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = 33871;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = 33871;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorRGB48 MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = 44000,
|
||||
G = 21354,
|
||||
B = 60080,
|
||||
A = 347,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorRGB9e5Tests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorRGB9e5>(), Is.EqualTo(4));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = 0.333;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = 0.333;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = 0.333;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = 0.333;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorRGB9e5 MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = 0.447,
|
||||
G = 0.224,
|
||||
B = 0.95,
|
||||
A = 0.897,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorRGBA16Tests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorRGBA16>(), Is.EqualTo(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorRGBA16 MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = 0b11010101,
|
||||
G = 0b01110010,
|
||||
B = 0b10001111,
|
||||
A = 0b11000111,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorRGBA32Tests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorRGBA32>(), Is.EqualTo(4));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = 0b01001110;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorRGBA32 MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = 0b11010101,
|
||||
G = 0b01110010,
|
||||
B = 0b10001111,
|
||||
A = 0b11000111,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorRGBA64Tests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorRGBA64>(), Is.EqualTo(8));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = 33871;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = 33871;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = 33871;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = 33871;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorRGBA64 MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = 44000,
|
||||
G = 21354,
|
||||
B = 60080,
|
||||
A = 347,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorRGBAHalfTests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorRGBAHalf>(), Is.EqualTo(8));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = (Half)0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = (Half)0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = (Half)0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = (Half)0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorRGBAHalf MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = (Half)0.447f,
|
||||
G = (Half)0.224f,
|
||||
B = (Half)0.95f,
|
||||
A = (Half)0.897f,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorRGBASingleTests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorRGBASingle>(), Is.EqualTo(16));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = 0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = 0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = 0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = 0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorRGBASingle MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = 0.447f,
|
||||
G = 0.224f,
|
||||
B = 0.95f,
|
||||
A = 0.897f,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorRGBHalfTests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorRGBHalf>(), Is.EqualTo(6));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = (Half)0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = (Half)0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = (Half)0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = (Half)0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorRGBHalf MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = (Half)0.447f,
|
||||
G = (Half)0.224f,
|
||||
B = (Half)0.95f,
|
||||
A = (Half)0.897f,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorRGBSingleTests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorRGBSingle>(), Is.EqualTo(12));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = 0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = 0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = 0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = 0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorRGBSingle MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = 0.447f,
|
||||
G = 0.224f,
|
||||
B = 0.95f,
|
||||
A = 0.897f,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorRGHalfTests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorRGHalf>(), Is.EqualTo(4));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = (Half)0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = (Half)0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = (Half)0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = (Half)0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorRGHalf MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = (Half)0.447f,
|
||||
G = (Half)0.224f,
|
||||
B = (Half)0.95f,
|
||||
A = (Half)0.897f,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorRGSingleTests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorRGSingle>(), Is.EqualTo(8));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = 0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = 0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = 0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = 0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorRGSingle MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = 0.447f,
|
||||
G = 0.224f,
|
||||
B = 0.95f,
|
||||
A = 0.897f,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorRHalfTests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorRHalf>(), Is.EqualTo(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = (Half)0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = (Half)0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = (Half)0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = (Half)0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorRHalf MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = (Half)0.447f,
|
||||
G = (Half)0.224f,
|
||||
B = (Half)0.95f,
|
||||
A = (Half)0.897f,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//This code is source generated. Do not edit manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
|
||||
public partial class ColorRSingleTests
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<ColorRSingle>(), Is.EqualTo(4));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.R = 0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var b = color.B;
|
||||
var a = color.A;
|
||||
color.G = 0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var a = color.A;
|
||||
color.B = 0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
var r = color.R;
|
||||
var g = color.G;
|
||||
var b = color.B;
|
||||
color.A = 0.333f;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
var color = MakeRandomColor();
|
||||
color.GetChannels(out var r, out var g, out var b, out var a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
});
|
||||
}
|
||||
|
||||
public static ColorRSingle MakeRandomColor()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
R = 0.447f,
|
||||
G = 0.224f,
|
||||
B = 0.95f,
|
||||
A = 0.897f,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
{
|
||||
public sealed class NewRgbTests
|
||||
{
|
||||
[Test]
|
||||
public void ConvertA8Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.alpha8");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.A8ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorA8, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertARGB16Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.argb4444");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.ARGB16ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorARGB16, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGB24Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rgb24");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.RGB24ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGB24, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGBA32Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rgba32");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.RGBA32ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGBA32, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertARGB32Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.argb32");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.ARGB32ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorARGB32, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGB16Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rgb565");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.RGB16ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGB16, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertR16Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.r16");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.R16ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorR16, ushort, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGBA16Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rgba4444");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.RGBA16ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGBA16, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRG16Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rg16");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.RG16ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRG16, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertR8Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.r8");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = RgbConverter.R8ToBGRA32(data.Slice(totalBytesRead), size, size, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorR8, byte, ColorBGRA32, byte>(data.Slice(totalBytesRead), size, size, out byte[] newDecodedData);
|
||||
totalBytesRead += bytesRead;
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRHalfTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rhalf");
|
||||
RgbConverter.RHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRHalf, Half, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGHalfTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rghalf");
|
||||
RgbConverter.RGHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGHalf, Half, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGBAHalfTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rgbahalf");
|
||||
RgbConverter.RGBAHalfToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGBAHalf, Half, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRFloatTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rfloat");
|
||||
RgbConverter.RFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRSingle, float, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGFloatTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rgfloat");
|
||||
RgbConverter.RGFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGSingle, float, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGBAFloatTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rgbafloat");
|
||||
RgbConverter.RGBAFloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGBASingle, float, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGB9e5FloatTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rgb9e5float");
|
||||
RgbConverter.RGB9e5FloatToBGRA32(data, 256, 256, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGB9e5, double, ColorBGRA32, byte>(data, 256, 256, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRG32Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rg32");
|
||||
RgbConverter.RG32ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRG32, ushort, ColorBGRA32, byte>(data, 512, 512, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGB48Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rgb48");
|
||||
RgbConverter.RGB48ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGB48, ushort, ColorBGRA32, byte>(data, 512, 512, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGBA64Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rgba64");
|
||||
RgbConverter.RGBA64ToBGRA32(data, 512, 512, out byte[] originalDecodedData);
|
||||
RgbConverter.Convert<ColorRGBA64, ushort, ColorBGRA32, byte>(data, 512, 512, out byte[] newDecodedData);
|
||||
Assert.That(originalDecodedData, Is.EqualTo(newDecodedData));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
{
|
||||
internal static class PathConstants
|
||||
{
|
||||
public const string ThisTestProjectFolder = "../../../";
|
||||
public const string RepositoryRootFolder = ThisTestProjectFolder + "../";
|
||||
public const string TestFilesFolder = RepositoryRootFolder + "TestFiles/";
|
||||
|
||||
public const string AstcTestFilesFolder = TestFilesFolder + "Astc/";
|
||||
public const string AtcTestFilesFolder = TestFilesFolder + "Atc/";
|
||||
public const string BcTestFilesFolder = TestFilesFolder + "Bc/";
|
||||
public const string DxtTestFilesFolder = TestFilesFolder + "Dxt/";
|
||||
public const string EtcTestFilesFolder = TestFilesFolder + "Etc/";
|
||||
public const string PvrtcTestFilesFolder = TestFilesFolder + "Pvrtc/";
|
||||
public const string RgbTestFilesFolder = TestFilesFolder + "Rgb/";
|
||||
public const string Yuy2TestFilesFolder = TestFilesFolder + "Yuy2/";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
{
|
||||
public sealed class PvrtcTests
|
||||
{
|
||||
[Test]
|
||||
public void DecompressPVRTCTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.PvrtcTestFilesFolder + "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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
{
|
||||
public sealed class RgbTests
|
||||
{
|
||||
[Test]
|
||||
public void ConvertA8Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.alpha8");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Rgb.RgbConverter.A8ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertARGB16Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.argb4444");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Rgb.RgbConverter.ARGB16ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGB24Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rgb24");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Rgb.RgbConverter.RGB24ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGBA32Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rgba32");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Rgb.RgbConverter.RGBA32ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertARGB32Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.argb32");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Rgb.RgbConverter.ARGB32ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGB16Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rgb565");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Rgb.RgbConverter.RGB16ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertR16Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.r16");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Rgb.RgbConverter.R16ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGBA16Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rgba4444");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Rgb.RgbConverter.RGBA16ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRG16Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rg16");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Rgb.RgbConverter.RG16ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertR8Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.r8");
|
||||
int totalBytesRead = 0;
|
||||
foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps
|
||||
{
|
||||
int bytesRead = Rgb.RgbConverter.R8ToBGRA32(data.Slice(totalBytesRead), size, size, out _);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
Assert.That(totalBytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRHalfTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rhalf");
|
||||
int bytesRead = Rgb.RgbConverter.RHalfToBGRA32(data, 256, 256, out _);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGHalfTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rghalf");
|
||||
int bytesRead = Rgb.RgbConverter.RGHalfToBGRA32(data, 256, 256, out _);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGBAHalfTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rgbahalf");
|
||||
int bytesRead = Rgb.RgbConverter.RGBAHalfToBGRA32(data, 256, 256, out _);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRFloatTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rfloat");
|
||||
int bytesRead = Rgb.RgbConverter.RFloatToBGRA32(data, 256, 256, out _);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGFloatTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rgfloat");
|
||||
int bytesRead = Rgb.RgbConverter.RGFloatToBGRA32(data, 256, 256, out _);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGBAFloatTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rgbafloat");
|
||||
int bytesRead = Rgb.RgbConverter.RGBAFloatToBGRA32(data, 256, 256, out _);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGB9e5FloatTest()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rgb9e5float");
|
||||
int bytesRead = Rgb.RgbConverter.RGB9e5FloatToBGRA32(data, 256, 256, out _);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRG32Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rg32");
|
||||
int bytesRead = Rgb.RgbConverter.RG32ToBGRA32(data, 512, 512, out _);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGB48Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rgb48");
|
||||
int bytesRead = Rgb.RgbConverter.RGB48ToBGRA32(data, 512, 512, out _);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConvertRGBA64Test()
|
||||
{
|
||||
ReadOnlySpan<byte> data = File.ReadAllBytes(PathConstants.RgbTestFilesFolder + "test.rgba64");
|
||||
int bytesRead = Rgb.RgbConverter.RGBA64ToBGRA32(data, 512, 512, out _);
|
||||
Assert.That(bytesRead, Is.EqualTo(data.Length));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
global using NUnit.Framework;
|
||||
@@ -0,0 +1,21 @@
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using AssetRipper.TextureDecoder.Yuy2;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests
|
||||
{
|
||||
public sealed class Yuy2Tests
|
||||
{
|
||||
private const int TestImageWidth = 256;
|
||||
private const int TestImageHeight = 256;
|
||||
|
||||
[Test]
|
||||
public void DecompressYUY2Test()
|
||||
{
|
||||
byte[] input = File.ReadAllBytes(PathConstants.Yuy2TestFilesFolder + "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));
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-12
@@ -1,17 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard2.0;net5.0;net6.0</TargetFrameworks>
|
||||
<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>1.0.1.0</Version>
|
||||
<AssemblyVersion>1.0.1.0</AssemblyVersion>
|
||||
<PackageId>TextureDecoder</PackageId>
|
||||
<Version>1.2.0.0</Version>
|
||||
<AssemblyVersion>1.2.0.0</AssemblyVersion>
|
||||
<PackageId>AssetRipper.TextureDecoder</PackageId>
|
||||
<PackageTags>C# Texture</PackageTags>
|
||||
<RepositoryUrl>https://github.com/AssetRipper/TextureDecoder</RepositoryUrl>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
@@ -20,18 +22,15 @@
|
||||
<Copyright>Copyright (c) 2022 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 Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
|
||||
<PackageReference Include="Half" Version="1.0.0" />
|
||||
<PackageReference Include="System.Memory" Version="4.5.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<DocumentationFile>bin\TextureDecoder.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,30 @@
|
||||
namespace AssetRipper.TextureDecoder.Astc
|
||||
{
|
||||
public static partial class AstcDecoder
|
||||
{
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace AssetRipper.TextureDecoder.Astc
|
||||
{
|
||||
public static partial class AstcDecoder
|
||||
{
|
||||
private struct IntSeqData
|
||||
{
|
||||
public int bits;
|
||||
public int nonbits;
|
||||
}
|
||||
}
|
||||
}
|
||||
+98
-95
@@ -1,104 +1,52 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace TextureDecoder.Astc
|
||||
namespace AssetRipper.TextureDecoder.Astc
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static class AstcDecoder
|
||||
public static partial class AstcDecoder
|
||||
{
|
||||
private unsafe struct BlockData
|
||||
public static int DecodeASTC(ReadOnlySpan<byte> input, int width, int height, int blockWidth, int blockHeight, out byte[] output)
|
||||
{
|
||||
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;
|
||||
public int weight_num; // max: 120
|
||||
public fixed int cem[4];
|
||||
public int cem_range;
|
||||
public int endpoint_value_num; // max: 32
|
||||
public fixed int endpoints[4 * 8];
|
||||
public fixed int weights[144 * 2];
|
||||
public fixed int partition[144];
|
||||
output = new byte[width * height * 4];
|
||||
return DecodeASTC(input, width, height, blockWidth, blockHeight, output);
|
||||
}
|
||||
|
||||
private struct IntSeqData
|
||||
{
|
||||
public int bits;
|
||||
public int nonbits;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="blockWidth"></param>
|
||||
/// <param name="blockHeight"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] DecodeASTC(byte[] input, int width, int height, int blockWidth, int blockHeight)
|
||||
{
|
||||
byte[] output = new byte[width * height * 4];
|
||||
DecodeASTC(input, width, height, blockWidth, blockHeight, output);
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="blockWidth"></param>
|
||||
/// <param name="blockHeight"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecodeASTC(byte[] input, int width, int height, int blockWidth, int blockHeight, byte[] output)
|
||||
public unsafe static int DecodeASTC(ReadOnlySpan<byte> input, int width, int height, int blockWidth, int blockHeight, Span<byte> output)
|
||||
{
|
||||
fixed (byte* inputPtr = input)
|
||||
{
|
||||
fixed (byte* outputPtr = output)
|
||||
{
|
||||
DecodeASTC(inputPtr, width, height, blockWidth, blockHeight, outputPtr);
|
||||
return DecodeASTC(inputPtr, width, height, blockWidth, blockHeight, outputPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="blockWidth"></param>
|
||||
/// <param name="blockHeight"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecodeASTC(byte* input, int width, int height, int blockWidth, int blockHeight, byte* output)
|
||||
private unsafe static int DecodeASTC(byte* input, int width, int height, int blockWidth, int blockHeight, byte* output)
|
||||
{
|
||||
int bcw = (width + blockWidth - 1) / blockWidth;
|
||||
int bch = (height + blockHeight - 1) / blockHeight;
|
||||
int clen_last = (width + blockWidth - 1) % blockWidth + 1;
|
||||
uint* buf = stackalloc uint[blockWidth * blockHeight];
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, input += 16)
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 16)
|
||||
{
|
||||
DecodeBlock(input, blockWidth, blockHeight, buf);
|
||||
DecodeBlock(input + inputOffset, blockWidth, blockHeight, buf);
|
||||
int clen = s < bcw - 1 ? blockWidth : clen_last;
|
||||
uint* outputPtr = (uint*)(output + (t * blockHeight * 4 * width + s * 4 * blockWidth));
|
||||
uint* bufPtr = buf;
|
||||
for (int i = 0, y = t * blockHeight; i < blockHeight && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++) outputPtr[j] = bufPtr[j];
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j] = bufPtr[j];
|
||||
}
|
||||
|
||||
outputPtr += width;
|
||||
bufPtr += blockWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
private unsafe static void DecodeBlock(byte* input, int blockWidth, int blockHeight, uint* output)
|
||||
@@ -194,23 +142,17 @@ namespace TextureDecoder.Astc
|
||||
|
||||
pBlock->weight_num = pBlock->width * pBlock->height;
|
||||
if (pBlock->dual_plane != 0)
|
||||
pBlock->weight_num *= 2;
|
||||
|
||||
int weight_bits, config_bits, cem_base = 0;
|
||||
|
||||
switch (WeightPrecTableA[pBlock->weight_range])
|
||||
{
|
||||
case 3:
|
||||
weight_bits = pBlock->weight_num * WeightPrecTableB[pBlock->weight_range] + (pBlock->weight_num * 8 + 4) / 5;
|
||||
break;
|
||||
case 5:
|
||||
weight_bits = pBlock->weight_num * WeightPrecTableB[pBlock->weight_range] + (pBlock->weight_num * 7 + 2) / 3;
|
||||
break;
|
||||
default:
|
||||
weight_bits = pBlock->weight_num * WeightPrecTableB[pBlock->weight_range];
|
||||
break;
|
||||
pBlock->weight_num *= 2;
|
||||
}
|
||||
|
||||
int config_bits, cem_base = 0;
|
||||
int weight_bits = WeightPrecTableA[pBlock->weight_range] switch
|
||||
{
|
||||
3 => pBlock->weight_num * WeightPrecTableB[pBlock->weight_range] + (pBlock->weight_num * 8 + 4) / 5,
|
||||
5 => pBlock->weight_num * WeightPrecTableB[pBlock->weight_range] + (pBlock->weight_num * 7 + 2) / 3,
|
||||
_ => pBlock->weight_num * WeightPrecTableB[pBlock->weight_range],
|
||||
};
|
||||
if (pBlock->part_num == 1)
|
||||
{
|
||||
pBlock->cem[0] = *((int*)(input + 1)) >> 5 & 0xf;
|
||||
@@ -273,19 +215,12 @@ namespace TextureDecoder.Astc
|
||||
|
||||
for (int i = 0, endpoint_bits; i < CemTableA.Length; i++)
|
||||
{
|
||||
switch (CemTableA[i])
|
||||
endpoint_bits = CemTableA[i] switch
|
||||
{
|
||||
case 3:
|
||||
endpoint_bits = pBlock->endpoint_value_num * CemTableB[i] + (pBlock->endpoint_value_num * 8 + 4) / 5;
|
||||
break;
|
||||
case 5:
|
||||
endpoint_bits = pBlock->endpoint_value_num * CemTableB[i] + (pBlock->endpoint_value_num * 7 + 2) / 3;
|
||||
break;
|
||||
default:
|
||||
endpoint_bits = pBlock->endpoint_value_num * CemTableB[i];
|
||||
break;
|
||||
}
|
||||
|
||||
3 => pBlock->endpoint_value_num * CemTableB[i] + (pBlock->endpoint_value_num * 8 + 4) / 5,
|
||||
5 => pBlock->endpoint_value_num * CemTableB[i] + (pBlock->endpoint_value_num * 7 + 2) / 3,
|
||||
_ => pBlock->endpoint_value_num * CemTableB[i],
|
||||
};
|
||||
if (endpoint_bits <= remain_bits)
|
||||
{
|
||||
pBlock->cem_range = i;
|
||||
@@ -364,35 +299,51 @@ namespace TextureDecoder.Astc
|
||||
{
|
||||
case 1:
|
||||
for (int i = 0; i < pBlock->endpoint_value_num; i++)
|
||||
{
|
||||
ev[i] = epSeq[i].bits * 0xff;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
for (int i = 0; i < pBlock->endpoint_value_num; i++)
|
||||
{
|
||||
ev[i] = epSeq[i].bits * 0x55;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
for (int i = 0; i < pBlock->endpoint_value_num; i++)
|
||||
{
|
||||
ev[i] = epSeq[i].bits << 5 | epSeq[i].bits << 2 | epSeq[i].bits >> 1;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
for (int i = 0; i < pBlock->endpoint_value_num; i++)
|
||||
{
|
||||
ev[i] = epSeq[i].bits << 4 | epSeq[i].bits;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
for (int i = 0; i < pBlock->endpoint_value_num; i++)
|
||||
{
|
||||
ev[i] = epSeq[i].bits << 3 | epSeq[i].bits >> 2;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
for (int i = 0; i < pBlock->endpoint_value_num; i++)
|
||||
{
|
||||
ev[i] = epSeq[i].bits << 2 | epSeq[i].bits >> 4;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
for (int i = 0; i < pBlock->endpoint_value_num; i++)
|
||||
{
|
||||
ev[i] = epSeq[i].bits << 1 | epSeq[i].bits >> 6;
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
for (int i = 0; i < pBlock->endpoint_value_num; i++)
|
||||
{
|
||||
ev[i] = epSeq[i].bits;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -427,27 +378,42 @@ namespace TextureDecoder.Astc
|
||||
break;
|
||||
case 8:
|
||||
if (v[0] + v[2] + v[4] <= v[1] + v[3] + v[5])
|
||||
{
|
||||
SetEndpoint(&pBlock->endpoints[cemOff], v[0], v[2], v[4], 255, v[1], v[3], v[5], 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetEndpointBlue(&pBlock->endpoints[cemOff], v[1], v[3], v[5], 255, v[0], v[2], v[4], 255);
|
||||
}
|
||||
|
||||
break;
|
||||
case 9:
|
||||
BitTransferSigned(&v[1], &v[0]);
|
||||
BitTransferSigned(&v[3], &v[2]);
|
||||
BitTransferSigned(&v[5], &v[4]);
|
||||
if (v[1] + v[3] + v[5] >= 0)
|
||||
{
|
||||
SetEndpointClamp(&pBlock->endpoints[cemOff], v[0], v[2], v[4], 255, v[0] + v[1], v[2] + v[3], v[4] + v[5], 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetEndpointBlueClamp(&pBlock->endpoints[cemOff], v[0] + v[1], v[2] + v[3], v[4] + v[5], 255, v[0], v[2], v[4], 255);
|
||||
}
|
||||
|
||||
break;
|
||||
case 10:
|
||||
SetEndpoint(&pBlock->endpoints[cemOff], 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])
|
||||
{
|
||||
SetEndpoint(&pBlock->endpoints[cemOff], v[0], v[2], v[4], v[6], v[1], v[3], v[5], v[7]);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetEndpointBlue(&pBlock->endpoints[cemOff], v[1], v[3], v[5], v[7], v[0], v[2], v[4], v[6]);
|
||||
}
|
||||
|
||||
break;
|
||||
case 13:
|
||||
BitTransferSigned(&v[1], &v[0]);
|
||||
@@ -455,9 +421,14 @@ namespace TextureDecoder.Astc
|
||||
BitTransferSigned(&v[5], &v[4]);
|
||||
BitTransferSigned(&v[7], &v[6]);
|
||||
if (v[1] + v[3] + v[5] >= 0)
|
||||
{
|
||||
SetEndpointClamp(&pBlock->endpoints[cemOff], 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
|
||||
{
|
||||
SetEndpointBlueClamp(&pBlock->endpoints[cemOff], 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;
|
||||
}
|
||||
}
|
||||
@@ -475,23 +446,38 @@ namespace TextureDecoder.Astc
|
||||
{
|
||||
case 1:
|
||||
for (int i = 0; i < block->weight_num; i++)
|
||||
{
|
||||
wv[i] = wSeq[i].bits != 0 ? 63 : 0;
|
||||
}
|
||||
|
||||
break;
|
||||
case 2:
|
||||
for (int i = 0; i < block->weight_num; i++)
|
||||
{
|
||||
wv[i] = wSeq[i].bits << 4 | wSeq[i].bits << 2 | wSeq[i].bits;
|
||||
}
|
||||
|
||||
break;
|
||||
case 3:
|
||||
for (int i = 0; i < block->weight_num; i++)
|
||||
{
|
||||
wv[i] = wSeq[i].bits << 3 | wSeq[i].bits;
|
||||
}
|
||||
|
||||
break;
|
||||
case 4:
|
||||
for (int i = 0; i < block->weight_num; i++)
|
||||
{
|
||||
wv[i] = wSeq[i].bits << 2 | wSeq[i].bits >> 2;
|
||||
}
|
||||
|
||||
break;
|
||||
case 5:
|
||||
for (int i = 0; i < block->weight_num; i++)
|
||||
{
|
||||
wv[i] = wSeq[i].bits << 1 | wSeq[i].bits >> 4;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < block->weight_num; i++)
|
||||
@@ -546,7 +532,10 @@ namespace TextureDecoder.Astc
|
||||
{
|
||||
case 1:
|
||||
for (int i = 0; i < block->weight_num; i++)
|
||||
{
|
||||
wv[i] = wSeq[i].nonbits * 28;
|
||||
}
|
||||
|
||||
break;
|
||||
case 2:
|
||||
for (int i = 0; i < block->weight_num; i++)
|
||||
@@ -739,7 +728,9 @@ namespace TextureDecoder.Astc
|
||||
private unsafe static void DecodeIntseq(byte* input, int offset, int a, int b, int count, bool reverse, IntSeqData* _out)
|
||||
{
|
||||
if (count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int n = 0;
|
||||
|
||||
@@ -896,15 +887,25 @@ namespace TextureDecoder.Astc
|
||||
{
|
||||
ulong mask = len == 64 ? 0xffffffffffffffff : (1UL << len) - 1;
|
||||
if (len < 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (bit >= 64)
|
||||
{
|
||||
return *((ulong*)(input + 8)) >> (bit - 64) & mask;
|
||||
}
|
||||
else if (bit <= 0)
|
||||
{
|
||||
return *((ulong*)input) << -bit & mask;
|
||||
}
|
||||
else if (bit + len <= 64)
|
||||
{
|
||||
return *((ulong*)input) >> bit & mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (*((ulong*)input) >> bit | *((ulong*)(input + 8)) << (64 - bit)) & mask;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -919,7 +920,9 @@ namespace TextureDecoder.Astc
|
||||
*b = (*b >> 1) | (*a & 0x80);
|
||||
*a = (*a >> 1) & 0x3f;
|
||||
if ((*a & 0x20) != 0)
|
||||
{
|
||||
*a -= 0x40;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -1,151 +1,115 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace TextureDecoder.Atc
|
||||
namespace AssetRipper.TextureDecoder.Atc
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static class AtcDecoder
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] DecompressAtcRgb4(byte[] input, int width, int height)
|
||||
public static int DecompressAtcRgb4(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
byte[] output = new byte[width * height * 4];
|
||||
DecompressAtcRgb4(input, width, height, output);
|
||||
return output;
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressAtcRgb4(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecompressAtcRgb4(byte[] input, int width, int height, byte[] output)
|
||||
public unsafe static int DecompressAtcRgb4(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
fixed (byte* inputPtr = input)
|
||||
{
|
||||
fixed (byte* outputPtr = output)
|
||||
{
|
||||
DecompressAtcRgb4(inputPtr, width, height, outputPtr);
|
||||
return DecompressAtcRgb4(inputPtr, width, height, outputPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecompressAtcRgb4(byte* input, int width, int height, byte* output)
|
||||
private unsafe static int DecompressAtcRgb4(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;
|
||||
uint* buf = stackalloc uint[16];
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, input += 8)
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 8)
|
||||
{
|
||||
DecodeAtcRgb4Block(input, buf);
|
||||
DecodeAtcRgb4Block(new ReadOnlySpan<byte>(input + inputOffset, 8), new Span<uint>(buf, 16));
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
|
||||
uint* bufPtr = buf;
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++) outputPtr[j] = bufPtr[j];
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j] = bufPtr[j];
|
||||
}
|
||||
|
||||
outputPtr += width;
|
||||
bufPtr += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] DecompressAtcRgba8(byte[] input, int width, int height)
|
||||
public static int DecompressAtcRgba8(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
byte[] output = new byte[width * height * 4];
|
||||
DecompressAtcRgba8(input, width, height, output);
|
||||
return output;
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressAtcRgba8(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecompressAtcRgba8(byte[] input, int width, int height, byte[] output)
|
||||
public unsafe static int DecompressAtcRgba8(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
fixed (byte* inputPtr = input)
|
||||
{
|
||||
fixed (byte* outputPtr = output)
|
||||
{
|
||||
DecompressAtcRgba8(inputPtr, width, height, outputPtr);
|
||||
return DecompressAtcRgba8(inputPtr, width, height, outputPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecompressAtcRgba8(byte* input, int width, int height, byte* output)
|
||||
private unsafe static int DecompressAtcRgba8(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;
|
||||
uint* buf = stackalloc uint[16];
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, input += 16)
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 16)
|
||||
{
|
||||
DecodeAtcRgba8Block(input, buf);
|
||||
DecodeAtcRgba8Block(new ReadOnlySpan<byte>(input + inputOffset, 16), new Span<uint>(buf, 16));
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
|
||||
uint* bufPtr = buf;
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++) outputPtr[j] = bufPtr[j];
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j] = bufPtr[j];
|
||||
}
|
||||
|
||||
outputPtr += width;
|
||||
bufPtr += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
private unsafe static void DecodeAtcRgb4Block(byte* input, uint* output)
|
||||
private unsafe static void DecodeAtcRgb4Block(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
{
|
||||
int* colors = stackalloc int[16];
|
||||
int c0 = *((ushort*)(input + 0));
|
||||
int c1 = *((ushort*)(input + 2));
|
||||
uint cindex = *((uint*)(input + 4));
|
||||
Span<int> colors = stackalloc int[16];
|
||||
int c0 = input.ReadAtOffset<ushort>(0);
|
||||
int c1 = input.ReadAtOffset<ushort>(2);
|
||||
uint cindex = input.ReadAtOffset<uint>(4);
|
||||
|
||||
DecodeColors(colors, c0, c1);
|
||||
|
||||
for (int i = 0; i < 4 * 4; i++)
|
||||
{
|
||||
uint cidx = cindex & 3;
|
||||
int cidx = unchecked((int)(cindex & 3));
|
||||
int cb = colors[cidx * 4 + 0];
|
||||
int cg = colors[cidx * 4 + 1];
|
||||
int cr = colors[cidx * 4 + 2];
|
||||
@@ -154,18 +118,18 @@ namespace TextureDecoder.Atc
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static void DecodeAtcRgba8Block(byte* input, uint* output)
|
||||
private unsafe static void DecodeAtcRgba8Block(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
{
|
||||
int* alphas = stackalloc int[16];
|
||||
ulong avalue = *((ulong*)input);
|
||||
Span<int> alphas = stackalloc int[16];
|
||||
ulong avalue = input.ReadAtOffset<ulong>(0);
|
||||
int a0 = unchecked((int)(avalue >> 0) & 0xFF);
|
||||
int a1 = unchecked((int)(avalue >> 8) & 0xFF);
|
||||
ulong aindex = avalue >> 16;
|
||||
|
||||
int* colors = stackalloc int[16];
|
||||
int c0 = *((ushort*)(input + 8));
|
||||
int c1 = *((ushort*)(input + 10));
|
||||
uint cindex = *((uint*)(input + 12));
|
||||
Span<int> colors = stackalloc int[16];
|
||||
int c0 = input.ReadAtOffset<ushort>(8);
|
||||
int c1 = input.ReadAtOffset<ushort>(10);
|
||||
uint cindex = input.ReadAtOffset<uint>(12);
|
||||
|
||||
DecodeColors(colors, c0, c1);
|
||||
DecodeAlphas(alphas, a0, a1);
|
||||
@@ -185,7 +149,13 @@ namespace TextureDecoder.Atc
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private unsafe static void DecodeColors(int* colors, int c0, int c1)
|
||||
private static T ReadAtOffset<T>(this ReadOnlySpan<byte> input, int offset) where T : unmanaged
|
||||
{
|
||||
return MemoryMarshal.Read<T>(input.Slice(offset));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private unsafe static void DecodeColors(Span<int> colors, int c0, int c1)
|
||||
{
|
||||
if ((c0 & 0x8000) == 0)
|
||||
{
|
||||
@@ -226,7 +196,7 @@ namespace TextureDecoder.Atc
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private unsafe static void DecodeAlphas(int* alphas, int a0, int a1)
|
||||
private unsafe static void DecodeAlphas(Span<int> alphas, int a0, int a1)
|
||||
{
|
||||
alphas[0] = a0;
|
||||
alphas[1] = a1;
|
||||
@@ -0,0 +1,249 @@
|
||||
namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
internal static class Bc6hTables
|
||||
{
|
||||
internal static byte[][] ActualBitsCount =
|
||||
{
|
||||
new byte[] {10, 7, 11, 11, 11, 9, 8, 8, 8, 6, 10, 11, 12, 16},
|
||||
new byte[] {5, 6, 5, 4, 4, 5, 6, 5, 5, 6, 10, 9, 8, 4},
|
||||
new byte[] {5, 6, 4, 5, 4, 5, 5, 6, 5, 6, 10, 9, 8, 4},
|
||||
new byte[] {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 =
|
||||
{
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 1, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 0, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 1},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 0, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 1, 1},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {0, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 1, 1},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 1, 1},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 1},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {1, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {1, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {1, 0, 0, 0},
|
||||
new byte[] {1, 1, 1, 0},
|
||||
new byte[] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 129, 1},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {129, 0, 0, 0},
|
||||
new byte[] {1, 1, 1, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 129, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 129, 1},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {1, 0, 0, 0},
|
||||
new byte[] {129, 1, 0, 0},
|
||||
new byte[] {1, 1, 1, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {129, 0, 0, 0},
|
||||
new byte[] {1, 1, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 1, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 0, 0, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 129, 1},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {1, 0, 0, 0},
|
||||
new byte[] {129, 0, 0, 0},
|
||||
new byte[] {1, 1, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 129, 0},
|
||||
new byte[] {0, 1, 1, 0},
|
||||
new byte[] {0, 1, 1, 0},
|
||||
new byte[] {0, 1, 1, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 129, 1},
|
||||
new byte[] {0, 1, 1, 0},
|
||||
new byte[] {0, 1, 1, 0},
|
||||
new byte[] {1, 1, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 1},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {129, 1, 1, 0},
|
||||
new byte[] {1, 0, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {1, 1, 1, 1},
|
||||
new byte[] {129, 1, 1, 1},
|
||||
new byte[] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 129, 1},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {1, 0, 0, 0},
|
||||
new byte[] {1, 1, 1, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 129, 1},
|
||||
new byte[] {1, 0, 0, 1},
|
||||
new byte[] {1, 0, 0, 1},
|
||||
new byte[] {1, 1, 0, 0}
|
||||
}
|
||||
};
|
||||
|
||||
internal static int[] AWeight3 = { 0, 9, 18, 27, 37, 46, 55, 64 };
|
||||
|
||||
internal static int[] AWeight4 = { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 };
|
||||
}
|
||||
@@ -0,0 +1,929 @@
|
||||
namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
internal static class Bc7Tables
|
||||
{
|
||||
internal static byte[][] bcdec_bc7_actual_bits_count =
|
||||
{
|
||||
new byte[] { 4, 6, 5, 7, 5, 7, 7, 5 },
|
||||
new byte[] { 0, 0, 0, 0, 6, 8, 7, 5 }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// There are 64 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[][][][] bcdec_bc7_partition_sets =
|
||||
{
|
||||
new byte[][][]
|
||||
{
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 1, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 0, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 1},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 0, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 1, 1},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {0, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 1, 1},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 1, 1},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 1},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {1, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {1, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {1, 0, 0, 0},
|
||||
new byte[] {1, 1, 1, 0},
|
||||
new byte[] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 129, 1},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {129, 0, 0, 0},
|
||||
new byte[] {1, 1, 1, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 129, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 129, 1},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {1, 0, 0, 0},
|
||||
new byte[] {129, 1, 0, 0},
|
||||
new byte[] {1, 1, 1, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {129, 0, 0, 0},
|
||||
new byte[] {1, 1, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 1, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 0, 0, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 129, 1},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {1, 0, 0, 0},
|
||||
new byte[] {129, 0, 0, 0},
|
||||
new byte[] {1, 1, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 129, 0},
|
||||
new byte[] {0, 1, 1, 0},
|
||||
new byte[] {0, 1, 1, 0},
|
||||
new byte[] {0, 1, 1, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 129, 1},
|
||||
new byte[] {0, 1, 1, 0},
|
||||
new byte[] {0, 1, 1, 0},
|
||||
new byte[] {1, 1, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 1},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {129, 1, 1, 0},
|
||||
new byte[] {1, 0, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {1, 1, 1, 1},
|
||||
new byte[] {129, 1, 1, 1},
|
||||
new byte[] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 129, 1},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {1, 0, 0, 0},
|
||||
new byte[] {1, 1, 1, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 129, 1},
|
||||
new byte[] {1, 0, 0, 1},
|
||||
new byte[] {1, 0, 0, 1},
|
||||
new byte[] {1, 1, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 0, 1},
|
||||
new byte[] {0, 1, 0, 1},
|
||||
new byte[] {0, 1, 0, 1},
|
||||
new byte[] {0, 1, 0, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {1, 1, 1, 1},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 0, 1},
|
||||
new byte[] {1, 0, 129, 0},
|
||||
new byte[] {0, 1, 0, 1},
|
||||
new byte[] {1, 0, 1, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 1, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {129, 1, 0, 0},
|
||||
new byte[] {1, 1, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 129, 1},
|
||||
new byte[] {1, 1, 0, 0},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {1, 1, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 0, 1},
|
||||
new byte[] {0, 1, 0, 1},
|
||||
new byte[] {129, 0, 1, 0},
|
||||
new byte[] {1, 0, 1, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 1, 0},
|
||||
new byte[] {1, 0, 0, 1},
|
||||
new byte[] {0, 1, 1, 0},
|
||||
new byte[] {1, 0, 0, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 0, 1},
|
||||
new byte[] {1, 0, 1, 0},
|
||||
new byte[] {1, 0, 1, 0},
|
||||
new byte[] {0, 1, 0, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 129, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {1, 1, 0, 0},
|
||||
new byte[] {1, 1, 1, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {129, 1, 0, 0},
|
||||
new byte[] {1, 0, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 129, 1},
|
||||
new byte[] {0, 0, 1, 0},
|
||||
new byte[] {0, 1, 0, 0},
|
||||
new byte[] {1, 1, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 129, 1},
|
||||
new byte[] {1, 0, 1, 1},
|
||||
new byte[] {1, 1, 0, 1},
|
||||
new byte[] {1, 1, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 129, 0},
|
||||
new byte[] {1, 0, 0, 1},
|
||||
new byte[] {1, 0, 0, 1},
|
||||
new byte[] {0, 1, 1, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 1, 1},
|
||||
new byte[] {1, 1, 0, 0},
|
||||
new byte[] {1, 1, 0, 0},
|
||||
new byte[] {0, 0, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 1, 0},
|
||||
new byte[] {0, 1, 1, 0},
|
||||
new byte[] {1, 0, 0, 1},
|
||||
new byte[] {1, 0, 0, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 1, 129, 0},
|
||||
new byte[] {0, 1, 1, 0},
|
||||
new byte[] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 0, 0},
|
||||
new byte[] {1, 1, 129, 0},
|
||||
new byte[] {0, 1, 0, 0},
|
||||
new byte[] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 129, 0},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {0, 0, 1, 0},
|
||||
new byte[] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 129, 0},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {0, 0, 1, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 1, 0, 0},
|
||||
new byte[] {129, 1, 1, 0},
|
||||
new byte[] {0, 1, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 1, 0},
|
||||
new byte[] {1, 1, 0, 0},
|
||||
new byte[] {1, 0, 0, 1},
|
||||
new byte[] {0, 0, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 1, 1},
|
||||
new byte[] {0, 1, 1, 0},
|
||||
new byte[] {1, 1, 0, 0},
|
||||
new byte[] {1, 0, 0, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 129, 0},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {1, 0, 0, 1},
|
||||
new byte[] {1, 1, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 129, 1},
|
||||
new byte[] {1, 0, 0, 1},
|
||||
new byte[] {1, 1, 0, 0},
|
||||
new byte[] {0, 1, 1, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 1, 0},
|
||||
new byte[] {1, 1, 0, 0},
|
||||
new byte[] {1, 1, 0, 0},
|
||||
new byte[] {1, 0, 0, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 1, 0},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {1, 0, 0, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 0},
|
||||
new byte[] {1, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 1},
|
||||
new byte[] {1, 0, 0, 0},
|
||||
new byte[] {1, 1, 1, 0},
|
||||
new byte[] {0, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {1, 1, 1, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 0, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 129, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {1, 1, 1, 1},
|
||||
new byte[] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 129, 0},
|
||||
new byte[] {0, 0, 1, 0},
|
||||
new byte[] {1, 1, 1, 0},
|
||||
new byte[] {1, 1, 1, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 0, 0},
|
||||
new byte[] {0, 1, 0, 0},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {0, 1, 1, 129}
|
||||
}
|
||||
},
|
||||
new byte[][][]
|
||||
{
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 1, 129},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 2, 2, 1},
|
||||
new byte[] {2, 2, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 129},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {130, 2, 1, 1},
|
||||
new byte[] {2, 2, 2, 1}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {2, 0, 0, 1},
|
||||
new byte[] {130, 2, 1, 1},
|
||||
new byte[] {2, 2, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 2, 2, 130},
|
||||
new byte[] {0, 0, 2, 2},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {129, 1, 2, 2},
|
||||
new byte[] {1, 1, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 1, 129},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 0, 2, 2},
|
||||
new byte[] {0, 0, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 2, 130},
|
||||
new byte[] {0, 0, 2, 2},
|
||||
new byte[] {1, 1, 1, 1},
|
||||
new byte[] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 1, 1},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {130, 2, 1, 1},
|
||||
new byte[] {2, 2, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {129, 1, 1, 1},
|
||||
new byte[] {2, 2, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {1, 1, 1, 1},
|
||||
new byte[] {129, 1, 1, 1},
|
||||
new byte[] {2, 2, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {1, 1, 129, 1},
|
||||
new byte[] {2, 2, 2, 2},
|
||||
new byte[] {2, 2, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 1, 2},
|
||||
new byte[] {0, 0, 129, 2},
|
||||
new byte[] {0, 0, 1, 2},
|
||||
new byte[] {0, 0, 1, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 1, 2},
|
||||
new byte[] {0, 1, 129, 2},
|
||||
new byte[] {0, 1, 1, 2},
|
||||
new byte[] {0, 1, 1, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 2, 2},
|
||||
new byte[] {0, 129, 2, 2},
|
||||
new byte[] {0, 1, 2, 2},
|
||||
new byte[] {0, 1, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 1, 129},
|
||||
new byte[] {0, 1, 1, 2},
|
||||
new byte[] {1, 1, 2, 2},
|
||||
new byte[] {1, 2, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 1, 129},
|
||||
new byte[] {2, 0, 0, 1},
|
||||
new byte[] {130, 2, 0, 0},
|
||||
new byte[] {2, 2, 2, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 129},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 1, 1, 2},
|
||||
new byte[] {1, 1, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 1, 129},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {130, 0, 0, 1},
|
||||
new byte[] {2, 2, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {1, 1, 2, 2},
|
||||
new byte[] {129, 1, 2, 2},
|
||||
new byte[] {1, 1, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 2, 130},
|
||||
new byte[] {0, 0, 2, 2},
|
||||
new byte[] {0, 0, 2, 2},
|
||||
new byte[] {1, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 1, 129},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {0, 2, 2, 2},
|
||||
new byte[] {0, 2, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 129},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {130, 2, 2, 1},
|
||||
new byte[] {2, 2, 2, 1}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 129, 1},
|
||||
new byte[] {0, 1, 2, 2},
|
||||
new byte[] {0, 1, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {1, 1, 0, 0},
|
||||
new byte[] {130, 2, 129, 0},
|
||||
new byte[] {2, 2, 1, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 2, 130},
|
||||
new byte[] {0, 129, 2, 2},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 1, 2},
|
||||
new byte[] {0, 0, 1, 2},
|
||||
new byte[] {129, 1, 2, 2},
|
||||
new byte[] {2, 2, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 1, 0},
|
||||
new byte[] {1, 2, 130, 1},
|
||||
new byte[] {129, 2, 2, 1},
|
||||
new byte[] {0, 1, 1, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 1, 129, 0},
|
||||
new byte[] {1, 2, 130, 1},
|
||||
new byte[] {1, 2, 2, 1}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 2, 2},
|
||||
new byte[] {1, 1, 0, 2},
|
||||
new byte[] {129, 1, 0, 2},
|
||||
new byte[] {0, 0, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 1, 0},
|
||||
new byte[] {0, 129, 1, 0},
|
||||
new byte[] {2, 0, 0, 2},
|
||||
new byte[] {2, 2, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 1, 1},
|
||||
new byte[] {0, 1, 2, 2},
|
||||
new byte[] {0, 1, 130, 2},
|
||||
new byte[] {0, 0, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {2, 0, 0, 0},
|
||||
new byte[] {130, 2, 1, 1},
|
||||
new byte[] {2, 2, 2, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 2},
|
||||
new byte[] {129, 1, 2, 2},
|
||||
new byte[] {1, 2, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 2, 2, 130},
|
||||
new byte[] {0, 0, 2, 2},
|
||||
new byte[] {0, 0, 1, 2},
|
||||
new byte[] {0, 0, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 1, 129},
|
||||
new byte[] {0, 0, 1, 2},
|
||||
new byte[] {0, 0, 2, 2},
|
||||
new byte[] {0, 2, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 2, 0},
|
||||
new byte[] {0, 129, 2, 0},
|
||||
new byte[] {0, 1, 130, 0},
|
||||
new byte[] {0, 1, 2, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {1, 1, 129, 1},
|
||||
new byte[] {2, 2, 130, 2},
|
||||
new byte[] {0, 0, 0, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 2, 0},
|
||||
new byte[] {1, 2, 0, 1},
|
||||
new byte[] {130, 0, 129, 2},
|
||||
new byte[] {0, 1, 2, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 2, 0},
|
||||
new byte[] {2, 0, 1, 2},
|
||||
new byte[] {129, 130, 0, 1},
|
||||
new byte[] {0, 1, 2, 0}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 1, 1},
|
||||
new byte[] {2, 2, 0, 0},
|
||||
new byte[] {1, 1, 130, 2},
|
||||
new byte[] {0, 0, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 1, 1},
|
||||
new byte[] {1, 1, 130, 2},
|
||||
new byte[] {2, 2, 0, 0},
|
||||
new byte[] {0, 0, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 0, 129},
|
||||
new byte[] {0, 1, 0, 1},
|
||||
new byte[] {2, 2, 2, 2},
|
||||
new byte[] {2, 2, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {130, 1, 2, 1},
|
||||
new byte[] {2, 1, 2, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 2, 2},
|
||||
new byte[] {1, 129, 2, 2},
|
||||
new byte[] {0, 0, 2, 2},
|
||||
new byte[] {1, 1, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 2, 130},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 0, 2, 2},
|
||||
new byte[] {0, 0, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 2, 2, 0},
|
||||
new byte[] {1, 2, 130, 1},
|
||||
new byte[] {0, 2, 2, 0},
|
||||
new byte[] {1, 2, 2, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 0, 1},
|
||||
new byte[] {2, 2, 130, 2},
|
||||
new byte[] {2, 2, 2, 2},
|
||||
new byte[] {0, 1, 0, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {2, 1, 2, 1},
|
||||
new byte[] {130, 1, 2, 1},
|
||||
new byte[] {2, 1, 2, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 0, 129},
|
||||
new byte[] {0, 1, 0, 1},
|
||||
new byte[] {0, 1, 0, 1},
|
||||
new byte[] {2, 2, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 2, 2, 130},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {0, 2, 2, 2},
|
||||
new byte[] {0, 1, 1, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 2},
|
||||
new byte[] {1, 129, 1, 2},
|
||||
new byte[] {0, 0, 0, 2},
|
||||
new byte[] {1, 1, 1, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {2, 129, 1, 2},
|
||||
new byte[] {2, 1, 1, 2},
|
||||
new byte[] {2, 1, 1, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 2, 2, 2},
|
||||
new byte[] {0, 129, 1, 1},
|
||||
new byte[] {0, 1, 1, 1},
|
||||
new byte[] {0, 2, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 2},
|
||||
new byte[] {1, 1, 1, 2},
|
||||
new byte[] {129, 1, 1, 2},
|
||||
new byte[] {0, 0, 0, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 1, 0},
|
||||
new byte[] {0, 129, 1, 0},
|
||||
new byte[] {0, 1, 1, 0},
|
||||
new byte[] {2, 2, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {2, 1, 129, 2},
|
||||
new byte[] {2, 1, 1, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 1, 0},
|
||||
new byte[] {0, 129, 1, 0},
|
||||
new byte[] {2, 2, 2, 2},
|
||||
new byte[] {2, 2, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 2, 2},
|
||||
new byte[] {0, 0, 1, 1},
|
||||
new byte[] {0, 0, 129, 1},
|
||||
new byte[] {0, 0, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 2, 2},
|
||||
new byte[] {1, 1, 2, 2},
|
||||
new byte[] {129, 1, 2, 2},
|
||||
new byte[] {0, 0, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {2, 129, 1, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 0, 0, 130},
|
||||
new byte[] {0, 0, 0, 1},
|
||||
new byte[] {0, 0, 0, 2},
|
||||
new byte[] {0, 0, 0, 129}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 2, 2, 2},
|
||||
new byte[] {1, 2, 2, 2},
|
||||
new byte[] {0, 2, 2, 2},
|
||||
new byte[] {129, 2, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 0, 129},
|
||||
new byte[] {2, 2, 2, 2},
|
||||
new byte[] {2, 2, 2, 2},
|
||||
new byte[] {2, 2, 2, 130}
|
||||
},
|
||||
new byte[][]
|
||||
{
|
||||
new byte[] {128, 1, 1, 129},
|
||||
new byte[] {2, 0, 1, 1},
|
||||
new byte[] {130, 2, 0, 1},
|
||||
new byte[] {2, 2, 2, 0}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
internal static int[] AWeight2 = { 0, 21, 43, 64 };
|
||||
|
||||
internal static int[] AWeight3 = { 0, 9, 18, 27, 37, 46, 55, 64 };
|
||||
|
||||
internal static int[] AWeight4 = { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 };
|
||||
|
||||
internal const byte bcdec_bc7_sModeHasPBits = 0b11001011;
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Bc
|
||||
{
|
||||
public static class BcDecoder
|
||||
{
|
||||
public static int DecompressBC1(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
|
||||
return DecompressBC1(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressBC1(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int inputOffset = 0;
|
||||
for (int i = 0; i < height; i += 4)
|
||||
{
|
||||
for (int j = 0; j < width; j += 4)
|
||||
{
|
||||
int outputOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRGBA32>();
|
||||
BcHelpers.DecompressBc1(input.Slice(inputOffset, DefineConstants.BCDEC_BC1_BLOCK_SIZE), output.Slice(outputOffset), width * 4);
|
||||
inputOffset += DefineConstants.BCDEC_BC1_BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
RgbConverter.Convert<ColorRGBA32, byte, ColorBGRA32, byte>(output, width, height, output);
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressBC2(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
|
||||
return DecompressBC2(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressBC2(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int inputOffset = 0;
|
||||
for (int i = 0; i < height; i += 4)
|
||||
{
|
||||
for (int j = 0; j < width; j += 4)
|
||||
{
|
||||
int outputOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRGBA32>();
|
||||
BcHelpers.DecompressBc2(input.Slice(inputOffset), output.Slice(outputOffset), width * 4);
|
||||
inputOffset += DefineConstants.BCDEC_BC2_BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
RgbConverter.Convert<ColorRGBA32, byte, ColorBGRA32, byte>(output, width, height, output);
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressBC3(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
|
||||
return DecompressBC3(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressBC3(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int inputOffset = 0;
|
||||
for (int i = 0; i < height; i += 4)
|
||||
{
|
||||
for (int j = 0; j < width; j += 4)
|
||||
{
|
||||
int outputOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRGBA32>();
|
||||
BcHelpers.DecompressBc3(input.Slice(inputOffset), output.Slice(outputOffset), width * 4);
|
||||
inputOffset += DefineConstants.BCDEC_BC3_BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
RgbConverter.Convert<ColorRGBA32, byte, ColorBGRA32, byte>(output, width, height, output);
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressBC4(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
|
||||
return DecompressBC4(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressBC4(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int inputOffset = 0;
|
||||
Span<byte> buffer = new byte[width * height * Unsafe.SizeOf<ColorR8>()];
|
||||
for (int i = 0; i < height; i += 4)
|
||||
{
|
||||
for (int j = 0; j < width; j += 4)
|
||||
{
|
||||
int bufferOffset = ((i * width) + j) * Unsafe.SizeOf<ColorR8>();
|
||||
BcHelpers.DecompressBc4(input.Slice(inputOffset), buffer.Slice(bufferOffset), width);
|
||||
inputOffset += DefineConstants.BCDEC_BC4_BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
RgbConverter.Convert<ColorR8, byte, ColorBGRA32, byte>(buffer, width, height, output);
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressBC5(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
|
||||
return DecompressBC5(input, width, height, output);
|
||||
}
|
||||
|
||||
public static int DecompressBC5(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int inputOffset = 0;
|
||||
Span<byte> buffer = new byte[width * height * Unsafe.SizeOf<ColorRG16>()];
|
||||
for (int i = 0; i < height; i += 4)
|
||||
{
|
||||
for (int j = 0; j < width; j += 4)
|
||||
{
|
||||
int bufferOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRG16>();
|
||||
BcHelpers.DecompressBc5(input.Slice(inputOffset), buffer.Slice(bufferOffset), width * 2);
|
||||
inputOffset += DefineConstants.BCDEC_BC5_BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
RgbConverter.Convert<ColorRG16, byte, ColorBGRA32, byte>(buffer, width, height, output);
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressBC6H(ReadOnlySpan<byte> input, int width, int height, bool isSigned, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
|
||||
return DecompressBC6H(input, width, height, isSigned, output);
|
||||
}
|
||||
|
||||
public unsafe static int DecompressBC6H(ReadOnlySpan<byte> input, int width, int height, bool isSigned, Span<byte> output)
|
||||
{
|
||||
int bytesRead;
|
||||
byte[] buffer = new byte[width * height * Unsafe.SizeOf<ColorRGBSingle>()];
|
||||
fixed (byte* inputPtr = input)
|
||||
{
|
||||
fixed (byte* bufferPtr = buffer)
|
||||
{
|
||||
bytesRead = DecompressBC6H(inputPtr, width, height, isSigned, bufferPtr);
|
||||
}
|
||||
}
|
||||
RgbConverter.Convert<ColorRGBSingle, float, ColorBGRA32, byte>(buffer, width, height, output);
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
private unsafe static int DecompressBC6H(byte* input, int width, int height, bool isSigned, byte* output)
|
||||
{
|
||||
int inputOffset = 0;
|
||||
for (int i = 0; i < height; i += 4)
|
||||
{
|
||||
for (int j = 0; j < width; j += 4)
|
||||
{
|
||||
int outputOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRGBSingle>();
|
||||
BcHelpers.DecompressBc6h_Float(input + inputOffset, output + outputOffset, width * 3, isSigned ? 1 : 0);
|
||||
inputOffset += DefineConstants.BCDEC_BC6H_BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressBC7(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<ColorBGRA32>()];
|
||||
return DecompressBC7(input, width, height, output);
|
||||
}
|
||||
|
||||
public unsafe static int DecompressBC7(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int bytesRead;
|
||||
fixed (byte* inputPtr = input)
|
||||
{
|
||||
fixed (byte* outputPtr = output)
|
||||
{
|
||||
bytesRead = DecompressBC7(inputPtr, width, height, outputPtr);
|
||||
}
|
||||
}
|
||||
RgbConverter.Convert<ColorRGBA32, byte, ColorBGRA32, byte>(output, width, height, output);
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
private unsafe static int DecompressBC7(byte* input, int width, int height, byte* output)
|
||||
{
|
||||
int inputOffset = 0;
|
||||
for (int i = 0; i < height; i += 4)
|
||||
{
|
||||
for (int j = 0; j < width; j += 4)
|
||||
{
|
||||
int outputOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRGBA32>();
|
||||
BcHelpers.DecompressBc7(input + inputOffset, output + outputOffset, width * 4);
|
||||
inputOffset += DefineConstants.BCDEC_BC7_BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
return inputOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,45 @@
|
||||
namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
internal struct BitStream
|
||||
{
|
||||
public ulong low;
|
||||
public ulong high;
|
||||
|
||||
public int ReadBits(int numBits)
|
||||
{
|
||||
uint mask = (uint)((1 << numBits) - 1);
|
||||
/* Read the low N bits */
|
||||
uint bits = (uint)(this.low & mask);
|
||||
|
||||
this.low >>= numBits;
|
||||
/* Put the low N bits of "high" into the high 64-N bits of "low". */
|
||||
this.low |= (this.high & mask) << ((sizeof(ulong) * 8) - numBits);
|
||||
this.high >>= numBits;
|
||||
|
||||
return (int)bits;
|
||||
}
|
||||
|
||||
public int ReadBit()
|
||||
{
|
||||
return this.ReadBits(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reversed bits pulling, used in BC6H decoding
|
||||
/// </summary>
|
||||
/// <param name="numBits"></param>
|
||||
/// <returns></returns>
|
||||
public int ReadBitsReversed(int numBits)
|
||||
{
|
||||
int bits = this.ReadBits(numBits);
|
||||
/* Reverse the bits. */
|
||||
int result = 0;
|
||||
while (numBits-- != 0)
|
||||
{
|
||||
result <<= 1;
|
||||
result |= bits & 1;
|
||||
bits >>= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
internal static class DefineConstants
|
||||
{
|
||||
public const int BCDEC_BC1_BLOCK_SIZE = 8;
|
||||
public const int BCDEC_BC2_BLOCK_SIZE = 16;
|
||||
public const int BCDEC_BC3_BLOCK_SIZE = 16;
|
||||
public const int BCDEC_BC4_BLOCK_SIZE = 8;
|
||||
public const int BCDEC_BC5_BLOCK_SIZE = 16;
|
||||
public const int BCDEC_BC6H_BLOCK_SIZE = 16;
|
||||
public const int BCDEC_BC7_BLOCK_SIZE = 16;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/* bcdec.h - v0.94
|
||||
provides functions to decompress blocks of BC compressed images
|
||||
written by Sergii "iOrange" Kudlai in 2022
|
||||
This library does not allocate memory and is trying to use as less stack as possible
|
||||
The library was never optimized specifically for speed but for the overall size
|
||||
it has zero external dependencies and is not using any runtime functions
|
||||
Supported BC formats:
|
||||
BC1 (also known as DXT1) + it's "binary alpha" variant BC1A (DXT1A)
|
||||
BC2 (also known as DXT3)
|
||||
BC3 (also known as DXT5)
|
||||
BC4 (also known as ATI1N)
|
||||
BC5 (also known as ATI2N)
|
||||
BC6H (HDR format)
|
||||
BC7
|
||||
BC1/BC2/BC3/BC7 are expected to decompress into 4*4 RGBA blocks 8bit per component (32bit pixel)
|
||||
BC4/BC5 are expected to decompress into 4*4 R/RG blocks 8bit per component (8bit and 16bit pixel)
|
||||
BC6H is expected to decompress into 4*4 RGB blocks of either 32bit float or 16bit "half" per
|
||||
component (96bit or 48bit pixel)
|
||||
For more info, issues and suggestions please visit https://github.com/iOrange/bcdec
|
||||
CREDITS:
|
||||
Aras Pranckevicius (@aras-p) - BC1/BC3 decoders optimizations (up to 3x the speed)
|
||||
- BC6H/BC7 bits pulling routines optimizations
|
||||
- Split BC6H decompression function into 'half' and
|
||||
'float' variants
|
||||
LICENSE: See end of file for license information.
|
||||
*/
|
||||
|
||||
|
||||
/* Used information sources:
|
||||
https://docs.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-block-compression
|
||||
https://docs.microsoft.com/en-us/windows/win32/direct3d11/bc6h-format
|
||||
https://docs.microsoft.com/en-us/windows/win32/direct3d11/bc7-format
|
||||
https://docs.microsoft.com/en-us/windows/win32/direct3d11/bc7-format-mode-reference
|
||||
! WARNING ! Khronos's BPTC partitions tables contain mistakes, do not use them!
|
||||
https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.html#BPTC
|
||||
! Use tables from here instead !
|
||||
https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_compression_bptc.txt
|
||||
Leaving it here as it's a nice read
|
||||
https://fgiesen.wordpress.com/2021/10/04/gpu-bcn-decoding/
|
||||
Fast half to float function from here
|
||||
https://gist.github.com/rygorous/2144712
|
||||
*/
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Bc;
|
||||
|
||||
/// <summary>
|
||||
/// Union between uint and float
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal struct FP32
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public uint u;
|
||||
[FieldOffset(0)]
|
||||
public float f;
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
using System.Buffers.Binary;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Dxt
|
||||
{
|
||||
public static class DxtDecoder
|
||||
{
|
||||
/// <summary>
|
||||
/// Decompress a DXT1 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer of size 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT1(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * sizeof(uint)];
|
||||
return DecompressDXT1(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT1 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT1(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output, 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];
|
||||
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);
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
colors[2] = Color((r0 + r1) / 2, (g0 + g1) / 2, (b0 + b1) / 2, 255);
|
||||
}
|
||||
|
||||
uint d = ToUInt32(input, offset + 4);
|
||||
for (int i = 0; i < 16; i++, d >>= 2)
|
||||
{
|
||||
buffer[i] = unchecked((uint)colors[d & 3]);
|
||||
}
|
||||
|
||||
int clen = (s < bcw - 1 ? 4 : clen_last) * 4;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT3 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer of size 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT3(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * sizeof(uint)];
|
||||
return DecompressDXT3(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT3 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT3(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output, 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];
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, offset += 16)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
colors[2] = Color((r0 + r1) / 2, (g0 + g1) / 2, (b0 + b1) / 2, 0);
|
||||
}
|
||||
|
||||
uint d = ToUInt32(input, offset + 12);
|
||||
for (int i = 0; i < 16; i++, d >>= 2)
|
||||
{
|
||||
buffer[i] = unchecked((uint)(colors[d & 3] | alphas[i]));
|
||||
}
|
||||
|
||||
int clen = (s < bcw - 1 ? 4 : clen_last) * 4;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT5 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer of size 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT5(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * sizeof(uint)];
|
||||
return DecompressDXT5(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT5 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT5(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output, 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];
|
||||
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])
|
||||
{
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
colors[2] = Color((r0 + r1) / 2, (g0 + g1) / 2, (b0 + b1) / 2, 0);
|
||||
}
|
||||
|
||||
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]));
|
||||
}
|
||||
|
||||
int clen = (s < bcw - 1 ? 4 : clen_last) * 4;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void Rgb565(int c, out int r, out int g, out int 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)]
|
||||
private static int Color(int r, int g, int b, int a)
|
||||
{
|
||||
return r << 16 | g << 8 | b | a << 24;
|
||||
}
|
||||
|
||||
/// <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="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>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void BlockCopy(ReadOnlySpan<byte> source, int sourceOffset, Span<byte> destination, int destinationOffset, int count)
|
||||
{
|
||||
source.Slice(sourceOffset, count).CopyTo(destination.Slice(destinationOffset));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static uint ToUInt32(ReadOnlySpan<byte> input, int offset)
|
||||
{
|
||||
return BinaryPrimitives.ReadUInt32LittleEndian(input.Slice(offset));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong ToUInt64(ReadOnlySpan<byte> input, int offset)
|
||||
{
|
||||
return BinaryPrimitives.ReadUInt64LittleEndian(input.Slice(offset));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,543 +1,421 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace TextureDecoder.Etc
|
||||
namespace AssetRipper.TextureDecoder.Etc
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static class EtcDecoder
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] DecompressETC(byte[] input, int width, int height)
|
||||
public static int DecompressETC(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
byte[] output = new byte[width * height * 4];
|
||||
DecompressETC(input, width, height, output);
|
||||
return output;
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressETC(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecompressETC(byte[] input, int width, int height, byte[] output)
|
||||
public unsafe static int DecompressETC(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
fixed (byte* inputPtr = input)
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
|
||||
if (input.Length < requiredLength)
|
||||
{
|
||||
fixed (byte* outputPtr = output)
|
||||
{
|
||||
DecompressETC(inputPtr, width, height, outputPtr);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecompressETC(byte* input, int width, int height, byte* output)
|
||||
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;
|
||||
uint* buf = stackalloc uint[16];
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, input += 8)
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 8)
|
||||
{
|
||||
DecodeEtc1Block(input, buf);
|
||||
DecodeEtc1Block(input.Slice(inputOffset, 8), buf);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
|
||||
uint* bufPtr = buf;
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++) outputPtr[j] = bufPtr[j];
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j] = buf[j + 4 * i];
|
||||
}
|
||||
|
||||
outputPtr += width;
|
||||
bufPtr += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] DecompressETC2(byte[] input, int width, int height)
|
||||
public static int DecompressETC2(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
byte[] output = new byte[width * height * 4];
|
||||
DecompressETC2(input, width, height, output);
|
||||
return output;
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressETC2(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecompressETC2(byte[] input, int width, int height, byte[] output)
|
||||
public unsafe static int DecompressETC2(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
fixed (byte* inputPtr = input)
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
|
||||
if (input.Length < requiredLength)
|
||||
{
|
||||
fixed (byte* outputPtr = output)
|
||||
{
|
||||
DecompressETC2(inputPtr, width, height, outputPtr);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecompressETC2(byte* input, int width, int height, byte* output)
|
||||
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;
|
||||
uint* buf = stackalloc uint[16];
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, input += 8)
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 8)
|
||||
{
|
||||
DecodeEtc2Block(input, buf);
|
||||
DecodeEtc2Block(input.Slice(inputOffset, 8), buf);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
|
||||
uint* bufPtr = buf;
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++) outputPtr[j] = bufPtr[j];
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j] = buf[j + 4 * i];
|
||||
}
|
||||
|
||||
outputPtr += width;
|
||||
bufPtr += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] DecompressETC2A1(byte[] input, int width, int height)
|
||||
public static int DecompressETC2A1(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
byte[] output = new byte[width * height * 4];
|
||||
DecompressETC2A1(input, width, height, output);
|
||||
return output;
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressETC2A1(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecompressETC2A1(byte[] input, int width, int height, byte[] output)
|
||||
public unsafe static int DecompressETC2A1(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
fixed (byte* inputPtr = input)
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
|
||||
if (input.Length < requiredLength)
|
||||
{
|
||||
fixed (byte* outputPtr = output)
|
||||
{
|
||||
DecompressETC2A1(inputPtr, width, height, outputPtr);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecompressETC2A1(byte* input, int width, int height, byte* output)
|
||||
private unsafe static int DecompressETC2A1(ReadOnlySpan<byte> input, int width, int height, byte* output)
|
||||
{
|
||||
int width4 = width * 4;
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
uint* buf = stackalloc uint[16];
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, input += 8)
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 8)
|
||||
{
|
||||
DecodeEtc2a1Block(input, buf);
|
||||
DecodeEtc2a1Block(input.Slice(inputOffset, 8), buf);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
|
||||
uint* bufPtr = buf;
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++) outputPtr[j] = bufPtr[j];
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j] = buf[j + 4 * i];
|
||||
}
|
||||
|
||||
outputPtr += width;
|
||||
bufPtr += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] DecompressETC2A8(byte[] input, int width, int height)
|
||||
public static int DecompressETC2A8(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
byte[] output = new byte[width * height * 4];
|
||||
DecompressETC2A8(input, width, height, output);
|
||||
return output;
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressETC2A8(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecompressETC2A8(byte[] input, int width, int height, byte[] output)
|
||||
public unsafe static int DecompressETC2A8(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
fixed (byte* inputPtr = input)
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 16;
|
||||
if (input.Length < requiredLength)
|
||||
{
|
||||
fixed (byte* outputPtr = output)
|
||||
{
|
||||
DecompressETC2A8(inputPtr, width, height, outputPtr);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecompressETC2A8(byte* input, int width, int height, byte* output)
|
||||
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;
|
||||
uint* buf = stackalloc uint[16];
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, input += 16)
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 16)
|
||||
{
|
||||
DecodeEtc2Block(input + 8, buf);
|
||||
DecodeEtc2a8Block(input, buf);
|
||||
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));
|
||||
uint* bufPtr = buf;
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++) outputPtr[j] = bufPtr[j];
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j] = buf[j + 4 * i];
|
||||
}
|
||||
|
||||
outputPtr += width;
|
||||
bufPtr += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] DecompressEACRUnsigned(byte[] input, int width, int height)
|
||||
public static int DecompressEACRUnsigned(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
byte[] output = new byte[width * height * 4];
|
||||
DecompressEACRUnsigned(input, width, height, output);
|
||||
return output;
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressEACRUnsigned(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecompressEACRUnsigned(byte[] input, int width, int height, byte[] output)
|
||||
public unsafe static int DecompressEACRUnsigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
fixed (byte* inputPtr = input)
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
|
||||
if (input.Length < requiredLength)
|
||||
{
|
||||
fixed (byte* outputPtr = output)
|
||||
{
|
||||
DecompressEACRUnsigned(inputPtr, width, height, outputPtr);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecompressEACRUnsigned(byte* input, int width, int height, byte* output)
|
||||
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;
|
||||
uint* buf = stackalloc uint[16];
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
buf[i] = 0xFF000000;
|
||||
}
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, input += 8)
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 8)
|
||||
{
|
||||
DecodeEacUnsignedBlock(input, buf, 2);
|
||||
DecodeEacUnsignedBlock(input.Slice(inputOffset, 8), buf, 2);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
|
||||
uint* bufPtr = buf;
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++) outputPtr[j] = bufPtr[j];
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j] = buf[j + 4 * i];
|
||||
}
|
||||
|
||||
outputPtr += width;
|
||||
bufPtr += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] DecompressEACRSigned(byte[] input, int width, int height)
|
||||
public static int DecompressEACRSigned(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
byte[] output = new byte[width * height * 4];
|
||||
DecompressEACRSigned(input, width, height, output);
|
||||
return output;
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressEACRSigned(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecompressEACRSigned(byte[] input, int width, int height, byte[] output)
|
||||
public unsafe static int DecompressEACRSigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
fixed (byte* inputPtr = input)
|
||||
int requiredLength = ((width + 3) / 4) * ((height + 3) / 4) * 8;
|
||||
if (input.Length < requiredLength)
|
||||
{
|
||||
fixed (byte* outputPtr = output)
|
||||
{
|
||||
DecompressEACRSigned(inputPtr, width, height, outputPtr);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecompressEACRSigned(byte* input, int width, int height, byte* output)
|
||||
|
||||
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;
|
||||
uint* buf = stackalloc uint[16];
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
buf[i] = 0xFF000000;
|
||||
}
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, input += 8)
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 8)
|
||||
{
|
||||
DecodeEacSignedBlock(input, buf, 2);
|
||||
DecodeEacSignedBlock(input.Slice(inputOffset, 8), buf, 2);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
|
||||
uint* bufPtr = buf;
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++) outputPtr[j] = bufPtr[j];
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j] = buf[j + 4 * i];
|
||||
}
|
||||
|
||||
outputPtr += width;
|
||||
bufPtr += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] DecompressEACRGUnsigned(byte[] input, int width, int height)
|
||||
public static int DecompressEACRGUnsigned(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
byte[] output = new byte[width * height * 4];
|
||||
DecompressEACRGUnsigned(input, width, height, output);
|
||||
return output;
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressEACRGUnsigned(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecompressEACRGUnsigned(byte[] input, int width, int height, byte[] output)
|
||||
public unsafe static int DecompressEACRGUnsigned(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
fixed (byte* inputPtr = input)
|
||||
int bcw = (width + 3) / 4;
|
||||
int bch = (height + 3) / 4;
|
||||
|
||||
if (input.Length < bcw * bch * 16)
|
||||
{
|
||||
fixed (byte* outputPtr = output)
|
||||
throw new ArgumentException($"{nameof(input)} has length {input.Length} which is less than the required length {bcw * bch * 16}", nameof(input));
|
||||
}
|
||||
|
||||
int clen_last = (width + 3) % 4 + 1;
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
buf[i] = 0xFF000000;
|
||||
}
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 16)
|
||||
{
|
||||
DecompressEACRGUnsigned(inputPtr, width, height, outputPtr);
|
||||
DecodeEacUnsignedBlock(input.Slice(inputOffset + 0, 8), buf, 2);
|
||||
DecodeEacUnsignedBlock(input.Slice(inputOffset + 8, 8), buf, 1);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
int outputOffset = t * 16 * width + s * 16;
|
||||
Span<uint> outputPtr = MemoryMarshal.Cast<byte, uint>(output.Slice(outputOffset));
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j + i * width] = buf[j + 4 * i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
public static int DecompressEACRGSigned(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return DecompressEACRGSigned(input, width, height, output);
|
||||
}
|
||||
|
||||
public unsafe 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecompressEACRGUnsigned(byte* input, int width, int height, byte* output)
|
||||
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;
|
||||
uint* buf = stackalloc uint[16];
|
||||
Span<uint> buf = stackalloc uint[16];
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
buf[i] = 0xFF000000;
|
||||
}
|
||||
int inputOffset = 0;
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, input += 16)
|
||||
for (int s = 0; s < bcw; s++, inputOffset += 16)
|
||||
{
|
||||
DecodeEacUnsignedBlock(input + 0, buf, 2);
|
||||
DecodeEacUnsignedBlock(input + 8, buf, 1);
|
||||
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));
|
||||
uint* bufPtr = buf;
|
||||
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++) outputPtr[j] = bufPtr[j];
|
||||
for (int j = 0; j < clen; j++)
|
||||
{
|
||||
outputPtr[j] = buf[j + 4 * i];
|
||||
}
|
||||
|
||||
outputPtr += width;
|
||||
bufPtr += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
return inputOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Parses an 8-byte Etc1 block from <paramref name="input"/>.
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] DecompressEACRGSigned(byte[] input, int width, int height)
|
||||
{
|
||||
byte[] output = new byte[width * height * 4];
|
||||
DecompressEACRGSigned(input, width, height, output);
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecompressEACRGSigned(byte[] input, int width, int height, byte[] output)
|
||||
{
|
||||
fixed (byte* inputPtr = input)
|
||||
{
|
||||
fixed (byte* outputPtr = output)
|
||||
{
|
||||
DecompressEACRGSigned(inputPtr, width, height, outputPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="output"></param>
|
||||
public unsafe static void DecompressEACRGSigned(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;
|
||||
uint* buf = stackalloc uint[16];
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
buf[i] = 0xFF000000;
|
||||
}
|
||||
for (int t = 0; t < bch; t++)
|
||||
{
|
||||
for (int s = 0; s < bcw; s++, input += 16)
|
||||
{
|
||||
DecodeEacSignedBlock(input + 0, buf, 2);
|
||||
DecodeEacSignedBlock(input + 8, buf, 1);
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
uint* outputPtr = (uint*)(output + (t * 16 * width + s * 16));
|
||||
uint* bufPtr = buf;
|
||||
for (int i = 0, y = t * 4; i < 4 && y < height; i++, y++)
|
||||
{
|
||||
for (int j = 0; j < clen; j++) outputPtr[j] = bufPtr[j];
|
||||
outputPtr += width;
|
||||
bufPtr += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static void DecodeEtc1Block(byte* input, uint* output)
|
||||
private unsafe static void DecodeEtc1Block(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
{
|
||||
byte i3 = input[3];
|
||||
int* code = stackalloc int[2];
|
||||
Span<int> code = stackalloc int[2];
|
||||
code[0] = (i3 >> 5) * 4;
|
||||
code[1] = (i3 >> 2 & 7) * 4;
|
||||
byte* c = stackalloc byte[6];
|
||||
Span<byte> c = stackalloc byte[6];
|
||||
int ti = (i3 & 1) * 16;
|
||||
if ((i3 & 2) != 0)
|
||||
{
|
||||
@@ -584,11 +462,11 @@ namespace TextureDecoder.Etc
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static void DecodeEtc2Block(byte* input, uint* output)
|
||||
private unsafe static void DecodeEtc2Block(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
{
|
||||
int j = input[6] << 8 | input[7];
|
||||
int k = input[4] << 8 | input[5];
|
||||
byte* c = stackalloc byte[3 * 3];
|
||||
Span<byte> c = stackalloc byte[3 * 3];
|
||||
|
||||
if ((input[3] & 2) != 0)
|
||||
{
|
||||
@@ -608,7 +486,7 @@ namespace TextureDecoder.Etc
|
||||
}
|
||||
int ti = input[3] >> 1 & 6 | input[3] & 1;
|
||||
byte d = Etc2DistanceTable[ti];
|
||||
uint* color_set = stackalloc uint[]
|
||||
ReadOnlySpan<uint> color_set = stackalloc uint[]
|
||||
{
|
||||
ApplicateColorRaw(c, 0),
|
||||
ApplicateColor(c, 1, d),
|
||||
@@ -649,7 +527,7 @@ namespace TextureDecoder.Etc
|
||||
++di;
|
||||
}
|
||||
byte d = Etc2DistanceTable[di];
|
||||
uint* color_set = stackalloc uint[]
|
||||
ReadOnlySpan<uint> color_set = stackalloc uint[]
|
||||
{
|
||||
ApplicateColor(c, 0, d),
|
||||
ApplicateColor(c, 0, -d),
|
||||
@@ -699,7 +577,7 @@ namespace TextureDecoder.Etc
|
||||
else
|
||||
{
|
||||
// differential
|
||||
int* code = stackalloc int[2];
|
||||
Span<int> code = stackalloc int[2];
|
||||
code[0] = (input[3] >> 5) * 4;
|
||||
code[1] = (input[3] >> 2 & 7) * 4;
|
||||
int ti = (input[3] & 1) * 16;
|
||||
@@ -732,7 +610,7 @@ namespace TextureDecoder.Etc
|
||||
else
|
||||
{
|
||||
// individual
|
||||
int* code = stackalloc int[2];
|
||||
Span<int> code = stackalloc int[2];
|
||||
code[0] = (input[3] >> 5) * 4;
|
||||
code[1] = (input[3] >> 2 & 7) * 4;
|
||||
int ti = (input[3] & 1) * 16;
|
||||
@@ -759,7 +637,7 @@ namespace TextureDecoder.Etc
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private unsafe static void DecodeEtc2a1Block(byte* input, uint* output)
|
||||
private unsafe static void DecodeEtc2a1Block(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
{
|
||||
if ((input[3] & 2) != 0)
|
||||
{
|
||||
@@ -772,11 +650,11 @@ namespace TextureDecoder.Etc
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static void DecodeEtc2PunchThrowBlock(byte* input, uint* output)
|
||||
private unsafe static void DecodeEtc2PunchThrowBlock(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
{
|
||||
int j = input[6] << 8 | input[7];
|
||||
int k = input[4] << 8 | input[5];
|
||||
byte* c = stackalloc byte[3 * 3];
|
||||
Span<byte> c = stackalloc byte[3 * 3];
|
||||
int r = input[0] & 0xf8;
|
||||
int dr = (input[0] << 3 & 0x18) - (input[0] << 3 & 0x20);
|
||||
if (r + dr < 0 || r + dr > 255)
|
||||
@@ -918,7 +796,7 @@ namespace TextureDecoder.Etc
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static void DecodeEtc2a8Block(byte* input, uint* output)
|
||||
private unsafe static void DecodeEtc2a8Block(ReadOnlySpan<byte> input, Span<uint> output)
|
||||
{
|
||||
int @base = input[0];
|
||||
int data1 = input[1];
|
||||
@@ -927,53 +805,53 @@ namespace TextureDecoder.Etc
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
DecodeEac11Block((byte*)output + 3, @base);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(3), @base);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int table = data1 & 0xF;
|
||||
ulong l = Get6SwapedBytes(input);
|
||||
DecodeEac11Block((byte*)output + 3, @base, table, mul, l);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(3), @base, table, mul, l);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static void DecodeEacUnsignedBlock(byte* input, uint* output, int channel)
|
||||
private unsafe static void DecodeEacUnsignedBlock(ReadOnlySpan<byte> input, Span<uint> output, int channel)
|
||||
{
|
||||
int @base = input[0];
|
||||
int data1 = input[1];
|
||||
int mul = data1 >> 4;
|
||||
if (mul == 0)
|
||||
{
|
||||
DecodeEac11Block((byte*)output + channel, @base);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(channel), @base);
|
||||
}
|
||||
else
|
||||
{
|
||||
int table = data1 & 0xF;
|
||||
ulong l = Get6SwapedBytes(input);
|
||||
DecodeEac11Block((byte*)output + channel, @base, table, mul, l);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(channel), @base, table, mul, l);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static void DecodeEacSignedBlock(byte* input, uint* output, int channel)
|
||||
private unsafe static void DecodeEacSignedBlock(ReadOnlySpan<byte> input, Span<uint> output, int channel)
|
||||
{
|
||||
int @base = 127 + unchecked((sbyte)input[0]);
|
||||
int data1 = input[1];
|
||||
int mul = data1 >> 4;
|
||||
if (mul == 0)
|
||||
{
|
||||
DecodeEac11Block((byte*)output + channel, @base);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(channel), @base);
|
||||
}
|
||||
else
|
||||
{
|
||||
int table = data1 & 0xF;
|
||||
ulong l = Get6SwapedBytes(input);
|
||||
DecodeEac11Block((byte*)output + channel, @base, table, mul, l);
|
||||
DecodeEac11Block(MemoryMarshal.Cast<uint, byte>(output).Slice(channel), @base, table, mul, l);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private unsafe static void DecodeEac11Block(byte* output, int @base)
|
||||
private unsafe static void DecodeEac11Block(Span<byte> output, int @base)
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
@@ -982,7 +860,7 @@ namespace TextureDecoder.Etc
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private unsafe static void DecodeEac11Block(byte* output, int @base, int ti, int mul, ulong l)
|
||||
private unsafe static void DecodeEac11Block(Span<byte> output, int @base, int ti, int mul, ulong l)
|
||||
{
|
||||
fixed (sbyte* table = &Etc2AlphaModTable[ti * 8])
|
||||
{
|
||||
@@ -1007,25 +885,25 @@ namespace TextureDecoder.Etc
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private unsafe static uint ApplicateColor(byte* c, int o, int m)
|
||||
private unsafe static uint ApplicateColor(ReadOnlySpan<byte> c, int o, int m)
|
||||
{
|
||||
return Color(Clamp255(c[o * 3 + 0] + m), Clamp255(c[o * 3 + 1] + m), Clamp255(c[o * 3 + 2] + m), 255);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private unsafe static uint ApplicateColor(int* c, int o, int m)
|
||||
private unsafe static uint ApplicateColor(ReadOnlySpan<int> c, int o, int m)
|
||||
{
|
||||
return Color(Clamp255(c[o * 3 + 0] + m), Clamp255(c[o * 3 + 1] + m), Clamp255(c[o * 3 + 2] + m), 255);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private unsafe static uint ApplicateColorRaw(byte* c, int o)
|
||||
private unsafe static uint ApplicateColorRaw(ReadOnlySpan<byte> c, int o)
|
||||
{
|
||||
return Color(c[o * 3 + 0], c[o * 3 + 1], c[o * 3 + 2], 255);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private unsafe static ulong Get6SwapedBytes(byte* data)
|
||||
private unsafe static ulong Get6SwapedBytes(ReadOnlySpan<byte> data)
|
||||
{
|
||||
return data[7] | (uint)data[6] << 8 | (uint)data[5] << 16 | (uint)data[4] << 24 | (ulong)data[3] << 32 | (ulong)data[2] << 40;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
global using System;
|
||||
global using System.Runtime.CompilerServices;
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace AssetRipper.TextureDecoder
|
||||
{
|
||||
internal static class OptimizationConstants
|
||||
{
|
||||
#if NETCOREAPP
|
||||
internal const MethodImplOptions AggressiveInliningAndOptimization = MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization;
|
||||
#else
|
||||
internal const MethodImplOptions AggressiveInliningAndOptimization = MethodImplOptions.AggressiveInlining;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
//#define DISABLE_TWINDDLING_ROUTINE
|
||||
#define ASSUME_IMAGE_TILING
|
||||
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
{
|
||||
public static partial class PvrtcDecoder
|
||||
{
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
//#define DISABLE_TWINDDLING_ROUTINE
|
||||
#define ASSUME_IMAGE_TILING
|
||||
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
{
|
||||
public static partial class PvrtcDecoder
|
||||
{
|
||||
/// <summary>
|
||||
/// Low precision colours extracted from the blocks
|
||||
/// </summary>
|
||||
private unsafe struct Colours5554
|
||||
{
|
||||
public fixed int Reps[2 * 4];
|
||||
}
|
||||
}
|
||||
}
|
||||
+60
-63
@@ -1,35 +1,26 @@
|
||||
//#define DISABLE_TWINDDLING_ROUTINE
|
||||
#define ASSUME_IMAGE_TILING
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace TextureDecoder.Pvrtc
|
||||
namespace AssetRipper.TextureDecoder.Pvrtc
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static class PvrtcDecoder
|
||||
public static partial class PvrtcDecoder
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Low precision colours extracted from the blocks
|
||||
/// Decompresses PVRTC to RGBA 8888
|
||||
/// </summary>
|
||||
private unsafe struct Colours5554
|
||||
/// <param name="input">The PVRTC texture data to decompress</param>
|
||||
/// <param name="xDim">X dimension (width) of the texture</param>
|
||||
/// <param name="yDim">Y dimension (height) of the texture</param>
|
||||
/// <param name="output">The decompressed texture data</param>
|
||||
/// <param name="do2bitMode">Signifies whether the data is PVRTC2 or PVRTC4</param>
|
||||
/// <returns>The number of bytes read from <paramref name="input"/></returns>
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int DecompressPVRTC(ReadOnlySpan<byte> input, int xDim, int yDim, bool do2bitMode, out byte[] output)
|
||||
{
|
||||
public fixed int Reps[2 * 4];
|
||||
output = new byte[xDim * yDim * sizeof(uint)];
|
||||
return DecompressPVRTC(input, xDim, yDim, do2bitMode, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -40,16 +31,9 @@ namespace TextureDecoder.Pvrtc
|
||||
/// <param name="yDim">Y dimension (height) of the texture</param>
|
||||
/// <param name="output">The decompressed texture data</param>
|
||||
/// <param name="do2bitMode">Signifies whether the data is PVRTC2 or PVRTC4</param>
|
||||
/// <returns></returns>
|
||||
public unsafe static void DecompressPVRTC(byte[] input, int xDim, int yDim, byte[] output, bool do2bitMode)
|
||||
{
|
||||
fixed (byte* inputPtr = input)
|
||||
{
|
||||
PVRDecompress(inputPtr, do2bitMode, xDim, yDim, output);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static void PVRDecompress(byte* compressedData, bool do2bitMode, int xDim, int yDim, byte[] output)
|
||||
/// <returns>The number of bytes read from <paramref name="input"/></returns>
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int DecompressPVRTC(ReadOnlySpan<byte> input, int xDim, int yDim, bool do2bitMode, Span<byte> output)
|
||||
{
|
||||
int xBlockSize = do2bitMode ? BlockX2bpp : BlockX4bpp;
|
||||
// for MBX don't allow the sizes to get too small
|
||||
@@ -61,13 +45,17 @@ namespace TextureDecoder.Pvrtc
|
||||
uint pblocki10 = uint.MaxValue;
|
||||
uint pblocki11 = uint.MaxValue;
|
||||
|
||||
Colours5554* m_colors = stackalloc Colours5554[2 * 2];
|
||||
uint maxBlockIndexRead = uint.MinValue;
|
||||
|
||||
Span<Colours5554> m_colors = stackalloc Colours5554[2 * 2];
|
||||
// interpolated A colors for the pixel
|
||||
int* aSig = stackalloc int[4];
|
||||
Span<int> aSig = stackalloc int[4];
|
||||
// interpolated B colors for the pixel
|
||||
int* bSig = stackalloc int[4];
|
||||
int* modulationVals = stackalloc int[8 * 16];
|
||||
int* modulationModes = stackalloc int[8 * 16];
|
||||
Span<int> bSig = stackalloc int[4];
|
||||
Span<int> modulationVals = stackalloc int[8 * 16];
|
||||
Span<int> modulationModes = stackalloc int[8 * 16];
|
||||
|
||||
ReadOnlySpan<AmtcBlock> blocks = MemoryMarshal.Cast<byte, AmtcBlock>(input);
|
||||
|
||||
// step through the pixels of the image decompressing each one in turn.
|
||||
// Note that this is a hideously inefficient way to do this!
|
||||
@@ -96,28 +84,30 @@ namespace TextureDecoder.Pvrtc
|
||||
bool changed = blocki00 != pblocki00 || blocki01 != pblocki01 || blocki10 != pblocki10 || blocki11 != pblocki11;
|
||||
if (changed)
|
||||
{
|
||||
AmtcBlock* block00 = &((AmtcBlock*)compressedData)[blocki00];
|
||||
Unpack5554Colour(block00, m_colors[0].Reps);
|
||||
AmtcBlock block00 = blocks[(int)blocki00];
|
||||
Unpack5554Colour(block00, m_colors.GetIntSpanForColor(0));
|
||||
UnpackModulations(block00, do2bitMode, modulationVals, modulationModes, 0, 0);
|
||||
AmtcBlock* block01 = &((AmtcBlock*)compressedData)[blocki01];
|
||||
Unpack5554Colour(block01, m_colors[1].Reps);
|
||||
AmtcBlock block01 = blocks[(int)blocki01];
|
||||
Unpack5554Colour(block01, m_colors.GetIntSpanForColor(1));
|
||||
UnpackModulations(block01, do2bitMode, modulationVals, modulationModes, xBlockSize, 0);
|
||||
AmtcBlock* block10 = &((AmtcBlock*)compressedData)[blocki10];
|
||||
Unpack5554Colour(block10, m_colors[2].Reps);
|
||||
AmtcBlock block10 = blocks[(int)blocki10];
|
||||
Unpack5554Colour(block10, m_colors.GetIntSpanForColor(2));
|
||||
UnpackModulations(block10, do2bitMode, modulationVals, modulationModes, 0, BlockYSize);
|
||||
AmtcBlock* block11 = &((AmtcBlock*)compressedData)[blocki11];
|
||||
Unpack5554Colour(block11, m_colors[3].Reps);
|
||||
AmtcBlock block11 = blocks[(int)blocki11];
|
||||
Unpack5554Colour(block11, m_colors.GetIntSpanForColor(3));
|
||||
UnpackModulations(block11, do2bitMode, modulationVals, modulationModes, xBlockSize, BlockYSize);
|
||||
|
||||
pblocki00 = blocki00;
|
||||
pblocki01 = blocki01;
|
||||
pblocki10 = blocki10;
|
||||
pblocki11 = blocki11;
|
||||
|
||||
maxBlockIndexRead = Math.Max(maxBlockIndexRead, Math.Max(Math.Max(blocki00, blocki01), Math.Max(blocki10, blocki11)));
|
||||
}
|
||||
|
||||
// decompress the pixel. First compute the interpolated A and B signals
|
||||
InterpolateColours(m_colors[0].Reps, m_colors[1].Reps, m_colors[2].Reps, m_colors[3].Reps, 0, do2bitMode, x, y, aSig);
|
||||
InterpolateColours(m_colors[0].Reps, m_colors[1].Reps, m_colors[2].Reps, m_colors[3].Reps, 1, do2bitMode, x, y, bSig);
|
||||
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);
|
||||
GetModulationValue(x, y, do2bitMode, modulationVals, modulationModes, out int mod, out bool doPT);
|
||||
|
||||
// compute the modulated color. Swap red and blue channel
|
||||
@@ -128,6 +118,8 @@ namespace TextureDecoder.Pvrtc
|
||||
output[position + 3] = doPT ? (byte)0 : (byte)((aSig[3] * 8 + mod * (bSig[3] - aSig[3])) >> 3);
|
||||
}
|
||||
}
|
||||
|
||||
return ((int)maxBlockIndexRead + 1) * Unsafe.SizeOf<AmtcBlock>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -211,15 +203,14 @@ namespace TextureDecoder.Pvrtc
|
||||
/// <summary>
|
||||
/// Get the modulation value as a numerator of a fraction of 8ths
|
||||
/// </summary>
|
||||
private unsafe static void GetModulationValue(int x, int y, bool do2bitMode, int* modulationVals, int* modulationModes, out int mod, out bool doPT)
|
||||
private static void GetModulationValue(int x, int y, bool do2bitMode, ReadOnlySpan<int> modulationVals, ReadOnlySpan<int> modulationModes, out int mod, out bool doPT)
|
||||
{
|
||||
// Map X and Y into the local 2x2 block
|
||||
y = (y & 0x3) | ((~y & 0x2) << 1);
|
||||
|
||||
if (do2bitMode)
|
||||
x = (x & 0x7) | ((~x & 0x4) << 1);
|
||||
else
|
||||
x = (x & 0x3) | ((~x & 0x2) << 1);
|
||||
x = do2bitMode
|
||||
? (x & 0x7) | ((~x & 0x4) << 1)
|
||||
: (x & 0x3) | ((~x & 0x2) << 1);
|
||||
|
||||
// assume no PT for now
|
||||
doPT = false;
|
||||
@@ -268,7 +259,7 @@ namespace TextureDecoder.Pvrtc
|
||||
/// This performs a HW bit accurate interpolation of either the A or B colours for a particular pixel
|
||||
/// <para>NOTE: It is assumed that the source colours are in ARGB 5554 format - This means that some "preparation" of the values will be necessary</para>
|
||||
/// </summary>
|
||||
private unsafe static void InterpolateColours(int* colorP, int* colorQ, int* colorR, int* colorS, int ci, bool do2bitMode, int x, int y, int* result)
|
||||
private static void InterpolateColours(ReadOnlySpan<int> colorP, ReadOnlySpan<int> colorQ, ReadOnlySpan<int> colorR, ReadOnlySpan<int> colorS, int ci, bool do2bitMode, int x, int y, Span<int> result)
|
||||
{
|
||||
// put the x and y values into the right range and get the u and v scale amounts
|
||||
int v = ((y & 0x3) | ((~y & 0x2) << 1)) - BlockYSize / 2;
|
||||
@@ -334,10 +325,10 @@ namespace TextureDecoder.Pvrtc
|
||||
/// <summary>
|
||||
/// Given the block and the texture type and it's relative position in the 2x2 group of blocks, extract the bit patterns for the fully defined pixels.
|
||||
/// </summary>
|
||||
private unsafe static void UnpackModulations(AmtcBlock* block, bool do2bitMode, int* modulationVals, int* modulationModes, int startX, int startY)
|
||||
private static void UnpackModulations(AmtcBlock block, bool do2bitMode, Span<int> modulationVals, Span<int> modulationModes, int startX, int startY)
|
||||
{
|
||||
int blockModMode = (int)(block->PackedData1 & 1);
|
||||
uint modulationBits = block->PackedData0;
|
||||
int blockModMode = (int)(block.PackedData1 & 1);
|
||||
uint modulationBits = block.PackedData0;
|
||||
|
||||
// if it's in an interpolated mode
|
||||
if (do2bitMode && (blockModMode != 0))
|
||||
@@ -405,14 +396,14 @@ namespace TextureDecoder.Pvrtc
|
||||
/// <summary>
|
||||
/// Given a block, extract the colour information and convert to 5554 formats
|
||||
/// </summary>
|
||||
private unsafe static void Unpack5554Colour(AmtcBlock* block, int* abColors)
|
||||
private static void Unpack5554Colour(AmtcBlock block, Span<int> abColors)
|
||||
{
|
||||
uint* rawBits = stackalloc uint[2];
|
||||
Span<uint> rawBits = stackalloc uint[2];
|
||||
// extract A and B
|
||||
// 15 bits (shifted up by one)
|
||||
rawBits[0] = block->PackedData1 & (0xFFFE);
|
||||
rawBits[0] = block.PackedData1 & (0xFFFE);
|
||||
// 16 bits
|
||||
rawBits[1] = block->PackedData1 >> 16;
|
||||
rawBits[1] = block.PackedData1 >> 16;
|
||||
|
||||
// step through both colours
|
||||
for (int i = 0; i < 2; i++)
|
||||
@@ -464,12 +455,18 @@ namespace TextureDecoder.Pvrtc
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
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)]
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
private static bool IsPowerOf2(uint input)
|
||||
{
|
||||
if (input == 0)
|
||||
@@ -484,7 +481,7 @@ namespace TextureDecoder.Pvrtc
|
||||
/// <summary>
|
||||
/// Define an expression to either wrap or clamp large or small vals to the legal coordinate range
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
private static int LimitCoord(int value, int size)
|
||||
{
|
||||
#if ASSUME_IMAGE_TILING
|
||||
@@ -0,0 +1,229 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb
|
||||
{
|
||||
internal static class ConversionUtilities
|
||||
{
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
internal static TTo ConvertValue<TFrom, TTo>(TFrom value)
|
||||
where TFrom : unmanaged
|
||||
where TTo : unmanaged
|
||||
{
|
||||
if (typeof(TFrom) == typeof(TTo))
|
||||
{
|
||||
return Unsafe.As<TFrom, TTo>(ref value);
|
||||
}
|
||||
else if(typeof(TFrom) == typeof(byte))
|
||||
{
|
||||
return ConvertUInt8<TTo>(Unsafe.As<TFrom, byte>(ref value));
|
||||
}
|
||||
else if (typeof(TFrom) == typeof(ushort))
|
||||
{
|
||||
return ConvertUInt16<TTo>(Unsafe.As<TFrom, ushort>(ref value));
|
||||
}
|
||||
else if (typeof(TFrom) == typeof(Half))
|
||||
{
|
||||
return ConvertHalf<TTo>(Unsafe.As<TFrom, Half>(ref value));
|
||||
}
|
||||
else if (typeof(TFrom) == typeof(float))
|
||||
{
|
||||
return ConvertSingle<TTo>(Unsafe.As<TFrom, float>(ref value));
|
||||
}
|
||||
else if (typeof(TFrom) == typeof(double))
|
||||
{
|
||||
return ConvertDouble<TTo>(Unsafe.As<TFrom, double>(ref value));
|
||||
}
|
||||
else
|
||||
{
|
||||
return default; //exceptions prevent inlining
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
private static TTo ConvertUInt8<TTo>(byte value) where TTo : unmanaged
|
||||
{
|
||||
if (typeof(TTo) == typeof(byte))
|
||||
{
|
||||
return Unsafe.As<byte, TTo>(ref value);
|
||||
}
|
||||
else if (typeof(TTo) == typeof(ushort))
|
||||
{
|
||||
ushort converted = unchecked((ushort)((uint)value << 8));
|
||||
return Unsafe.As<ushort, TTo>(ref converted);
|
||||
}
|
||||
else if (typeof(TTo) == typeof(Half))
|
||||
{
|
||||
Half converted = (Half)(value / (float)byte.MaxValue);
|
||||
return Unsafe.As<Half, TTo>(ref converted);
|
||||
}
|
||||
else if (typeof(TTo) == typeof(float))
|
||||
{
|
||||
float converted = (float)(value / (float)byte.MaxValue);
|
||||
return Unsafe.As<float, TTo>(ref converted);
|
||||
}
|
||||
else if (typeof(TTo) == typeof(double))
|
||||
{
|
||||
double converted = (double)(value / (double)byte.MaxValue);
|
||||
return Unsafe.As<double, TTo>(ref converted);
|
||||
}
|
||||
else
|
||||
{
|
||||
return default; //exceptions prevent inlining
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
private static TTo ConvertUInt16<TTo>(ushort value) where TTo : unmanaged
|
||||
{
|
||||
if (typeof(TTo) == typeof(byte))
|
||||
{
|
||||
byte converted = unchecked((byte)((uint)value >> 8));
|
||||
return Unsafe.As<byte, TTo>(ref converted);
|
||||
}
|
||||
else if (typeof(TTo) == typeof(ushort))
|
||||
{
|
||||
return Unsafe.As<ushort, TTo>(ref value);
|
||||
}
|
||||
else if (typeof(TTo) == typeof(Half))
|
||||
{
|
||||
Half converted = (Half)(value / (float)ushort.MaxValue);
|
||||
return Unsafe.As<Half, TTo>(ref converted);
|
||||
}
|
||||
else if (typeof(TTo) == typeof(float))
|
||||
{
|
||||
float converted = (float)(value / (float)ushort.MaxValue);
|
||||
return Unsafe.As<float, TTo>(ref converted);
|
||||
}
|
||||
else if (typeof(TTo) == typeof(double))
|
||||
{
|
||||
double converted = (double)(value / (double)ushort.MaxValue);
|
||||
return Unsafe.As<double, TTo>(ref converted);
|
||||
}
|
||||
else
|
||||
{
|
||||
return default; //exceptions prevent inlining
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
private static TTo ConvertHalf<TTo>(Half value) where TTo : unmanaged
|
||||
{
|
||||
if (typeof(TTo) == typeof(byte))
|
||||
{
|
||||
byte converted = ClampUInt8((float)value * byte.MaxValue);
|
||||
return Unsafe.As<byte, TTo>(ref converted);
|
||||
}
|
||||
else if (typeof(TTo) == typeof(ushort))
|
||||
{
|
||||
ushort converted = ClampUInt16((float)value * ushort.MaxValue);
|
||||
return Unsafe.As<ushort, TTo>(ref converted);
|
||||
}
|
||||
else if (typeof(TTo) == typeof(Half))
|
||||
{
|
||||
return Unsafe.As<Half, TTo>(ref value);
|
||||
}
|
||||
else if (typeof(TTo) == typeof(float))
|
||||
{
|
||||
float converted = (float)value;
|
||||
return Unsafe.As<float, TTo>(ref converted);
|
||||
}
|
||||
else if (typeof(TTo) == typeof(double))
|
||||
{
|
||||
double converted = (double)value;
|
||||
return Unsafe.As<double, TTo>(ref converted);
|
||||
}
|
||||
else
|
||||
{
|
||||
return default; //exceptions prevent inlining
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
private static TTo ConvertSingle<TTo>(float value) where TTo : unmanaged
|
||||
{
|
||||
if (typeof(TTo) == typeof(byte))
|
||||
{
|
||||
byte converted = ClampUInt8(value * byte.MaxValue);
|
||||
return Unsafe.As<byte, TTo>(ref converted);
|
||||
}
|
||||
else if (typeof(TTo) == typeof(ushort))
|
||||
{
|
||||
ushort converted = ClampUInt16(value * ushort.MaxValue);
|
||||
return Unsafe.As<ushort, TTo>(ref converted);
|
||||
}
|
||||
else if (typeof(TTo) == typeof(Half))
|
||||
{
|
||||
Half converted = (Half)value;
|
||||
return Unsafe.As<Half, TTo>(ref converted);
|
||||
}
|
||||
else if (typeof(TTo) == typeof(float))
|
||||
{
|
||||
return Unsafe.As<float, TTo>(ref value);
|
||||
}
|
||||
else if (typeof(TTo) == typeof(double))
|
||||
{
|
||||
double converted = (double)value;
|
||||
return Unsafe.As<double, TTo>(ref converted);
|
||||
}
|
||||
else
|
||||
{
|
||||
return default; //exceptions prevent inlining
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
private static TTo ConvertDouble<TTo>(double value) where TTo : unmanaged
|
||||
{
|
||||
if (typeof(TTo) == typeof(byte))
|
||||
{
|
||||
byte converted = ClampUInt8(value * byte.MaxValue);
|
||||
return Unsafe.As<byte, TTo>(ref converted);
|
||||
}
|
||||
else if (typeof(TTo) == typeof(ushort))
|
||||
{
|
||||
ushort converted = ClampUInt16(value * ushort.MaxValue);
|
||||
return Unsafe.As<ushort, TTo>(ref converted);
|
||||
}
|
||||
else if (typeof(TTo) == typeof(Half))
|
||||
{
|
||||
Half converted = (Half)value;
|
||||
return Unsafe.As<Half, TTo>(ref converted);
|
||||
}
|
||||
else if (typeof(TTo) == typeof(float))
|
||||
{
|
||||
float converted = (float)value;
|
||||
return Unsafe.As<float, TTo>(ref converted);
|
||||
}
|
||||
else if (typeof(TTo) == typeof(double))
|
||||
{
|
||||
return Unsafe.As<double, TTo>(ref value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return default; //exceptions prevent inlining
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
private static byte ClampUInt8(float x)
|
||||
{
|
||||
return byte.MaxValue < x ? byte.MaxValue : (x > byte.MinValue ? (byte)x : byte.MinValue);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
private static byte ClampUInt8(double x)
|
||||
{
|
||||
return byte.MaxValue < x ? byte.MaxValue : (x > byte.MinValue ? (byte)x : byte.MinValue);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
private static ushort ClampUInt16(float x)
|
||||
{
|
||||
return ushort.MaxValue < x ? ushort.MaxValue : (x > ushort.MinValue ? (ushort)x : ushort.MinValue);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
private static ushort ClampUInt16(double x)
|
||||
{
|
||||
return ushort.MaxValue < x ? ushort.MaxValue : (x > ushort.MinValue ? (ushort)x : ushort.MinValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb
|
||||
{
|
||||
internal static class DefaultColorMethods
|
||||
{
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
internal static void GetChannels<T, TArg>(T color, out TArg r, out TArg g, out TArg b, out TArg a)
|
||||
where TArg : unmanaged
|
||||
where T : unmanaged, IColor<TArg>
|
||||
{
|
||||
r = color.R;
|
||||
g = color.G;
|
||||
b = color.B;
|
||||
a = color.A;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
internal static void SetChannels<T, TArg>(ref T color, TArg r, TArg g, TArg b, TArg a)
|
||||
where TArg : unmanaged
|
||||
where T : unmanaged, IColor<TArg>
|
||||
{
|
||||
color.R = r;
|
||||
color.G = g;
|
||||
color.B = b;
|
||||
color.A = a;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public struct ColorA8 : IColor<byte>
|
||||
{
|
||||
public byte R
|
||||
{
|
||||
get => byte.MinValue;
|
||||
set { }
|
||||
}
|
||||
public byte G
|
||||
{
|
||||
get => byte.MinValue;
|
||||
set { }
|
||||
}
|
||||
public byte B
|
||||
{
|
||||
get => byte.MinValue;
|
||||
set { }
|
||||
}
|
||||
public byte A { get; set; }
|
||||
|
||||
public void GetChannels(out byte r, out byte g, out byte b, out byte a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public struct ColorARGB16 : IColor<byte>
|
||||
{
|
||||
private byte gb;
|
||||
private byte ar;
|
||||
|
||||
public byte B
|
||||
{
|
||||
get => unchecked((byte)((uint)gb << 4));
|
||||
set => gb = unchecked((byte)(((uint)value >> 4) | (gb & 0xF0u)));
|
||||
}
|
||||
public byte G
|
||||
{
|
||||
get => (byte)(gb & 0xF0);
|
||||
set => gb = (byte)((value & 0xF0u) | (gb & 0x0Fu));
|
||||
}
|
||||
public byte R
|
||||
{
|
||||
get => unchecked((byte)(ar << 4));
|
||||
set => ar = unchecked((byte)(((uint)value >> 4) | (ar & 0xF0u)));
|
||||
}
|
||||
public byte A
|
||||
{
|
||||
get => (byte)(ar & 0xF0);
|
||||
set => ar = (byte)((value & 0xF0u) | (ar & 0x0Fu));
|
||||
}
|
||||
|
||||
public void GetChannels(out byte r, out byte g, out byte b, out byte a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public struct ColorARGB32 : IColor<byte>
|
||||
{
|
||||
public byte A { get; set; }
|
||||
public byte R { get; set; }
|
||||
public byte G { get; set; }
|
||||
public byte B { get; set; }
|
||||
|
||||
public void GetChannels(out byte r, out byte g, out byte b, out byte a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public struct ColorBGRA32 : IColor<byte>
|
||||
{
|
||||
public byte B { get; set; }
|
||||
public byte G { get; set; }
|
||||
public byte R { get; set; }
|
||||
public byte A { get; set; }
|
||||
|
||||
public void GetChannels(out byte r, out byte g, out byte b, out byte a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public struct ColorR16 : IColor<ushort>
|
||||
{
|
||||
public ushort R { get; set; }
|
||||
public ushort G
|
||||
{
|
||||
get => ushort.MinValue;
|
||||
set { }
|
||||
}
|
||||
public ushort B
|
||||
{
|
||||
get => ushort.MinValue;
|
||||
set { }
|
||||
}
|
||||
public ushort A
|
||||
{
|
||||
get => ushort.MaxValue;
|
||||
set { }
|
||||
}
|
||||
|
||||
public void GetChannels(out ushort r, out ushort g, out ushort b, out ushort a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(ushort r, ushort g, ushort b, ushort a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public struct ColorR8 : IColor<byte>
|
||||
{
|
||||
public byte R { get; set; }
|
||||
public byte G
|
||||
{
|
||||
get => byte.MinValue;
|
||||
set { }
|
||||
}
|
||||
public byte B
|
||||
{
|
||||
get => byte.MinValue;
|
||||
set { }
|
||||
}
|
||||
public byte A
|
||||
{
|
||||
get => byte.MaxValue;
|
||||
set { }
|
||||
}
|
||||
|
||||
public void GetChannels(out byte r, out byte g, out byte b, out byte a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public struct ColorRG16 : IColor<byte>
|
||||
{
|
||||
public byte R { get; set; }
|
||||
public byte G { get; set; }
|
||||
public byte B
|
||||
{
|
||||
get => byte.MinValue;
|
||||
set { }
|
||||
}
|
||||
public byte A
|
||||
{
|
||||
get => byte.MaxValue;
|
||||
set { }
|
||||
}
|
||||
|
||||
public void GetChannels(out byte r, out byte g, out byte b, out byte a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public struct ColorRG32 : IColor<ushort>
|
||||
{
|
||||
public ushort R { get; set; }
|
||||
public ushort G { get; set; }
|
||||
public ushort B
|
||||
{
|
||||
get => ushort.MinValue;
|
||||
set { }
|
||||
}
|
||||
public ushort A
|
||||
{
|
||||
get => ushort.MaxValue;
|
||||
set { }
|
||||
}
|
||||
|
||||
public void GetChannels(out ushort r, out ushort g, out ushort b, out ushort a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(ushort r, ushort g, ushort b, ushort a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// Also called RGB 565
|
||||
/// </summary>
|
||||
public struct ColorRGB16 : IColor<byte>
|
||||
{
|
||||
private ushort bits;
|
||||
|
||||
/// <summary>
|
||||
/// 5 bits
|
||||
/// </summary>
|
||||
public byte R
|
||||
{
|
||||
get => (byte)(((uint)bits >> 8) & 0xF8);
|
||||
set
|
||||
{
|
||||
bits = (ushort)((((uint)value << 8) & 0xF800u) | (bits & ~0xF800u));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 6 bits
|
||||
/// </summary>
|
||||
public byte G
|
||||
{
|
||||
get => (byte)(((uint)bits >> 3) & 0xFC);
|
||||
set
|
||||
{
|
||||
bits = (ushort)((((uint)value << 3) & 0x07E0u) | (bits & ~0x07E0u));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 5 bits
|
||||
/// </summary>
|
||||
public byte B
|
||||
{
|
||||
get => (byte)(((uint)bits << 3) & 0xF8);
|
||||
set
|
||||
{
|
||||
bits = (ushort)((((uint)value >> 3) & 0x001Fu) | (bits & ~0x001Fu));
|
||||
}
|
||||
}
|
||||
|
||||
public byte A
|
||||
{
|
||||
get => byte.MaxValue;
|
||||
set { }
|
||||
}
|
||||
|
||||
public void GetChannels(out byte r, out byte g, out byte b, out byte a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
bits = (ushort)(((r & 0xF8u) << 8) | ((g & 0xFCu) << 3) | ((b & 0xF8u) >> 3));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public struct ColorRGB24 : IColor<byte>
|
||||
{
|
||||
public byte R { get; set; }
|
||||
public byte G { get; set; }
|
||||
public byte B { get; set; }
|
||||
public byte A
|
||||
{
|
||||
get => byte.MaxValue;
|
||||
set { }
|
||||
}
|
||||
|
||||
public void GetChannels(out byte r, out byte g, out byte b, out byte a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public struct ColorRGB48 : IColor<ushort>
|
||||
{
|
||||
public ushort R { get; set; }
|
||||
public ushort G { get; set; }
|
||||
public ushort B { get; set; }
|
||||
public ushort A
|
||||
{
|
||||
get => ushort.MaxValue;
|
||||
set { }
|
||||
}
|
||||
|
||||
public void GetChannels(out ushort r, out ushort g, out ushort b, out ushort a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(ushort r, ushort g, ushort b, ushort a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using static System.Math;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
/// <summary>
|
||||
/// 9 bits each for RGB and 5 bits for an exponent
|
||||
/// </summary>
|
||||
public struct ColorRGB9e5 : IColor<double>
|
||||
{
|
||||
private uint bits;
|
||||
|
||||
public double R
|
||||
{
|
||||
get => RBits * Scale;
|
||||
set
|
||||
{
|
||||
GetChannels(out _, out double g, out double b, out _);
|
||||
SetChannels(value, g, b, default);
|
||||
}
|
||||
}
|
||||
|
||||
public double G
|
||||
{
|
||||
get => GBits * Scale;
|
||||
set
|
||||
{
|
||||
GetChannels(out double r, out _, out double b, out _);
|
||||
SetChannels(r, value, b, default);
|
||||
}
|
||||
}
|
||||
|
||||
public double B
|
||||
{
|
||||
get => BBits * Scale;
|
||||
set
|
||||
{
|
||||
GetChannels(out double r, out double g, out _, out _);
|
||||
SetChannels(r, g, value, default);
|
||||
}
|
||||
}
|
||||
|
||||
public double A
|
||||
{
|
||||
get => 1;
|
||||
set { }
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public void GetChannels(out double r, out double g, out double b, out double a)
|
||||
{
|
||||
double scale = Scale;
|
||||
r = RBits * scale;
|
||||
g = GBits * scale;
|
||||
b = BBits * scale;
|
||||
a = 1;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public void SetChannels(double r, double g, double b, double a)
|
||||
{
|
||||
int exponent = CalculateExponent(r, g, b);
|
||||
double scale = Pow(2, exponent);
|
||||
uint rBits = (uint)(r / scale) & 0x1FF;
|
||||
uint gBits = (uint)(g / scale) & 0x1FF;
|
||||
uint bBits = (uint)(b / scale) & 0x1FF;
|
||||
uint exponentBits = unchecked((uint)(exponent + 24));
|
||||
bits = (exponentBits << 27) | (bBits << 18) | (gBits << 9) | (rBits << 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Range: -24 to 7 inclusive
|
||||
/// </summary>
|
||||
private int Exponent => unchecked((int)(bits >> 27) - 24);
|
||||
private double Scale => Pow(2, Exponent);
|
||||
private uint RBits => (bits >> 0) & 0x1FF;
|
||||
private uint GBits => (bits >> 9) & 0x1FF;
|
||||
private uint BBits => (bits >> 18) & 0x1FF;
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
private static int CalculateExponent(double r, double g, double b)
|
||||
{
|
||||
double maxChannel = Max(r, Max(g, b));
|
||||
double minExponent = Log2(maxChannel / 0x1FF);
|
||||
return (int)Ceiling(minExponent);
|
||||
}
|
||||
|
||||
#if NETSTANDARD
|
||||
private static readonly double LogTwoConstant = Log(2);
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
private static double Log2(double value) => Log(value) / LogTwoConstant;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public struct ColorRGBA16 : IColor<byte>
|
||||
{
|
||||
private byte ba;
|
||||
private byte rg;
|
||||
|
||||
public byte B
|
||||
{
|
||||
get => (byte)(ba & 0xF0u);
|
||||
set => ba = (byte)((value & 0xF0u) | (ba & 0x0Fu));
|
||||
}
|
||||
public byte G
|
||||
{
|
||||
get => unchecked((byte)(rg << 4));
|
||||
set => rg = unchecked((byte)(((uint)value >> 4) | (rg & 0xF0u)));
|
||||
}
|
||||
public byte R
|
||||
{
|
||||
get => (byte)(rg & 0xF0u);
|
||||
set => rg = (byte)((value & 0xF0u) | (rg & 0x0Fu));
|
||||
}
|
||||
public byte A
|
||||
{
|
||||
get => unchecked((byte)(ba << 4));
|
||||
set => ba = unchecked((byte)(((uint)value >> 4) | (ba & 0xF0u)));
|
||||
}
|
||||
|
||||
public void GetChannels(out byte r, out byte g, out byte b, out byte a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public struct ColorRGBA32 : IColor<byte>
|
||||
{
|
||||
public byte R { get; set; }
|
||||
public byte G { get; set; }
|
||||
public byte B { get; set; }
|
||||
public byte A { get; set; }
|
||||
|
||||
public void GetChannels(out byte r, out byte g, out byte b, out byte a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public struct ColorRGBA64 : IColor<ushort>
|
||||
{
|
||||
public ushort R { get; set; }
|
||||
public ushort G { get; set; }
|
||||
public ushort B { get; set; }
|
||||
public ushort A { get; set; }
|
||||
|
||||
public void GetChannels(out ushort r, out ushort g, out ushort b, out ushort a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(ushort r, ushort g, ushort b, ushort a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public struct ColorRGBAHalf : IColor<Half>
|
||||
{
|
||||
public Half R { get; set; }
|
||||
public Half G { get; set; }
|
||||
public Half B { get; set; }
|
||||
public Half A { get; set; }
|
||||
|
||||
public void GetChannels(out Half r, out Half g, out Half b, out Half a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(Half r, Half g, Half b, Half a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public struct ColorRGBASingle : IColor<float>
|
||||
{
|
||||
public float R { get; set; }
|
||||
public float G { get; set; }
|
||||
public float B { get; set; }
|
||||
public float A { get; set; }
|
||||
|
||||
public void GetChannels(out float r, out float g, out float b, out float a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(float r, float g, float b, float a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public struct ColorRGBHalf : IColor<Half>
|
||||
{
|
||||
public Half R { get; set; }
|
||||
public Half G { get; set; }
|
||||
public Half B { get; set; }
|
||||
public Half A
|
||||
{
|
||||
get => (Half)1;
|
||||
set { }
|
||||
}
|
||||
|
||||
public void GetChannels(out Half r, out Half g, out Half b, out Half a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(Half r, Half g, Half b, Half a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public struct ColorRGBSingle : IColor<float>
|
||||
{
|
||||
public float R { get; set; }
|
||||
public float G { get; set; }
|
||||
public float B { get; set; }
|
||||
public float A
|
||||
{
|
||||
get => 1;
|
||||
set { }
|
||||
}
|
||||
|
||||
public void GetChannels(out float r, out float g, out float b, out float a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(float r, float g, float b, float a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public struct ColorRGHalf : IColor<Half>
|
||||
{
|
||||
public Half R { get; set; }
|
||||
public Half G { get; set; }
|
||||
public Half B
|
||||
{
|
||||
get => default;
|
||||
set { }
|
||||
}
|
||||
public Half A
|
||||
{
|
||||
get => (Half)1;
|
||||
set { }
|
||||
}
|
||||
|
||||
public void GetChannels(out Half r, out Half g, out Half b, out Half a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(Half r, Half g, Half b, Half a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public struct ColorRGSingle : IColor<float>
|
||||
{
|
||||
public float R { get; set; }
|
||||
public float G { get; set; }
|
||||
public float B
|
||||
{
|
||||
get => default;
|
||||
set { }
|
||||
}
|
||||
public float A
|
||||
{
|
||||
get => 1;
|
||||
set { }
|
||||
}
|
||||
|
||||
public void GetChannels(out float r, out float g, out float b, out float a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(float r, float g, float b, float a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public struct ColorRHalf : IColor<Half>
|
||||
{
|
||||
public Half R { get; set; }
|
||||
public Half G
|
||||
{
|
||||
get => default;
|
||||
set { }
|
||||
}
|
||||
public Half B
|
||||
{
|
||||
get => default;
|
||||
set { }
|
||||
}
|
||||
public Half A
|
||||
{
|
||||
get => (Half)1;
|
||||
set { }
|
||||
}
|
||||
|
||||
public void GetChannels(out Half r, out Half g, out Half b, out Half a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(Half r, Half g, Half b, Half a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Formats
|
||||
{
|
||||
public struct ColorRSingle : IColor<float>
|
||||
{
|
||||
public float R { get; set; }
|
||||
public float G
|
||||
{
|
||||
get => default;
|
||||
set { }
|
||||
}
|
||||
public float B
|
||||
{
|
||||
get => default;
|
||||
set { }
|
||||
}
|
||||
public float A
|
||||
{
|
||||
get => 1;
|
||||
set { }
|
||||
}
|
||||
|
||||
public void GetChannels(out float r, out float g, out float b, out float a)
|
||||
{
|
||||
DefaultColorMethods.GetChannels(this, out r, out g, out b, out a);
|
||||
}
|
||||
|
||||
public void SetChannels(float r, float g, float b, float a)
|
||||
{
|
||||
DefaultColorMethods.SetChannels(ref this, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb
|
||||
{
|
||||
public interface IColor<T> where T : unmanaged
|
||||
{
|
||||
/// <summary>
|
||||
/// The red channel
|
||||
/// </summary>
|
||||
T R { get; set; }
|
||||
/// <summary>
|
||||
/// The green channel
|
||||
/// </summary>
|
||||
T G { get; set; }
|
||||
/// <summary>
|
||||
/// The blue channel
|
||||
/// </summary>
|
||||
T B { get; set; }
|
||||
/// <summary>
|
||||
/// The alpha channel
|
||||
/// </summary>
|
||||
T A { get; set; }
|
||||
|
||||
void GetChannels(out T r, out T g, out T b, out T a);
|
||||
void SetChannels(T r, T g, T b, T a);
|
||||
}
|
||||
|
||||
internal static class ColorExtensions
|
||||
{
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
internal static void SetConvertedChannels<TThis, TThisArg, TSourceArg>(this ref TThis color, TSourceArg r, TSourceArg g, TSourceArg b, TSourceArg a)
|
||||
where TThisArg : unmanaged
|
||||
where TSourceArg : unmanaged
|
||||
where TThis : unmanaged, IColor<TThisArg>
|
||||
{
|
||||
color.SetChannels(
|
||||
ConversionUtilities.ConvertValue<TSourceArg, TThisArg>(r),
|
||||
ConversionUtilities.ConvertValue<TSourceArg, TThisArg>(g),
|
||||
ConversionUtilities.ConvertValue<TSourceArg, TThisArg>(b),
|
||||
ConversionUtilities.ConvertValue<TSourceArg, TThisArg>(a));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,645 @@
|
||||
using System.Buffers.Binary;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb
|
||||
{
|
||||
public static class RgbConverter
|
||||
{
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int A8ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return A8ToBGRA32(input, width, height, output);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int A8ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int io = 0;
|
||||
int oo = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
output[oo + 0] = 0; // b
|
||||
output[oo + 1] = 0; // g
|
||||
output[oo + 2] = 0; // r
|
||||
output[oo + 3] = input[io]; // a
|
||||
oo += 4;
|
||||
io++;
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int ARGB16ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return ARGB16ToBGRA32(input, width, height, output);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int ARGB16ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int io = 0;
|
||||
int oo = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
output[oo + 0] = unchecked((byte)(input[io + 0] << 4)); // b
|
||||
output[oo + 1] = (byte)(input[io + 0] & 0xF0); // g
|
||||
output[oo + 2] = unchecked((byte)(input[io + 1] << 4)); // r
|
||||
output[oo + 3] = (byte)(input[io + 1] & 0xF0); // a
|
||||
io += 2;
|
||||
oo += 4;
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGB24ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return RGB24ToBGRA32(input, width, height, output);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGB24ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int io = 0;
|
||||
int oo = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
output[oo + 0] = input[io + 2]; // b
|
||||
output[oo + 1] = input[io + 1]; // g
|
||||
output[oo + 2] = input[io + 0]; // r
|
||||
output[oo + 3] = 255; // a
|
||||
io += 3;
|
||||
oo += 4;
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGBA32ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return RGBA32ToBGRA32(input, width, height, output);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGBA32ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int io = 0;
|
||||
int oo = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
output[oo + 0] = input[io + 2]; // b
|
||||
output[oo + 1] = input[io + 1]; // g
|
||||
output[oo + 2] = input[io + 0]; // r
|
||||
output[oo + 3] = input[io + 3]; // a
|
||||
io += 4;
|
||||
oo += 4;
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int ARGB32ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return ARGB32ToBGRA32(input, width, height, output);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int ARGB32ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int io = 0;
|
||||
int oo = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
output[oo + 0] = input[io + 3]; // b
|
||||
output[oo + 1] = input[io + 2]; // g
|
||||
output[oo + 2] = input[io + 1]; // r
|
||||
output[oo + 3] = input[io + 0]; // a
|
||||
io += 4;
|
||||
oo += 4;
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGB16ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return RGB16ToBGRA32(input, width, height, output);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGB16ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int io = 0;
|
||||
int oo = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
byte r = (byte)(input[io + 1] & 0xF8);
|
||||
byte g = unchecked((byte)((input[io + 1] << 5) | ((input[io + 0] & 0xE0) >> 3)));
|
||||
byte b = unchecked((byte)(input[io + 0] << 3));
|
||||
output[oo + 0] = b; // b
|
||||
output[oo + 1] = g; // g
|
||||
output[oo + 2] = r; // r
|
||||
output[oo + 3] = 255; // a
|
||||
io += 2;
|
||||
oo += 4;
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int R16ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return R16ToBGRA32(input, width, height, output);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int R16ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int io = 0;
|
||||
int oo = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
output[oo + 0] = 0; // b
|
||||
output[oo + 1] = 0; // g
|
||||
output[oo + 2] = input[io + 1]; // r
|
||||
output[oo + 3] = 255; // a
|
||||
io += 2;
|
||||
oo += 4;
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGBA16ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return RGBA16ToBGRA32(input, width, height, output);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGBA16ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int io = 0;
|
||||
int oo = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
output[oo + 0] = (byte)(input[io + 0] & 0xF0); // b
|
||||
output[oo + 1] = unchecked((byte)(input[io + 1] << 4)); // g
|
||||
output[oo + 2] = (byte)(input[io + 1] & 0xF0); // r
|
||||
output[oo + 3] = unchecked((byte)(input[io + 0] << 4)); // a
|
||||
io += 2;
|
||||
oo += 4;
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RG16ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return RG16ToBGRA32(input, width, height, output);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RG16ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int io = 0;
|
||||
int oo = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
output[oo + 0] = 0; // b
|
||||
output[oo + 1] = input[io + 1]; // g
|
||||
output[oo + 2] = input[io + 0]; // r
|
||||
output[oo + 3] = 255; // a
|
||||
io += 2;
|
||||
oo += 4;
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int R8ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return R8ToBGRA32(input, width, height, output);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int R8ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int io = 0;
|
||||
int oo = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
output[oo + 0] = 0; // b
|
||||
output[oo + 1] = 0; // g
|
||||
output[oo + 2] = input[io + 0]; // r
|
||||
output[oo + 3] = 255; // a
|
||||
io += 1;
|
||||
oo += 4;
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RHalfToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return RHalfToBGRA32(input, width, height, output);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RHalfToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int io = 0;
|
||||
int oo = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
byte r = ClampByte(ToHalf(input, io) * 255f);
|
||||
output[oo + 0] = 0; // b
|
||||
output[oo + 1] = 0; // g
|
||||
output[oo + 2] = r; // r
|
||||
output[oo + 3] = 255; // a
|
||||
io += 2;
|
||||
oo += 4;
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGHalfToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return RGHalfToBGRA32(input, width, height, output);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGHalfToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int io = 0;
|
||||
int oo = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
byte r = ClampByte(ToHalf(input, io + 0) * 255f);
|
||||
byte g = ClampByte(ToHalf(input, io + 2) * 255f);
|
||||
output[oo + 0] = 0; // b
|
||||
output[oo + 1] = g; // g
|
||||
output[oo + 2] = r; // r
|
||||
output[oo + 3] = 255; // a
|
||||
io += 4;
|
||||
oo += 4;
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGBAHalfToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return RGBAHalfToBGRA32(input, width, height, output);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGBAHalfToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int io = 0;
|
||||
int oo = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
byte r = ClampByte(ToHalf(input, io + 0) * 255f);
|
||||
byte g = ClampByte(ToHalf(input, io + 2) * 255f);
|
||||
byte b = ClampByte(ToHalf(input, io + 4) * 255f);
|
||||
byte a = ClampByte(ToHalf(input, io + 6) * 255f);
|
||||
output[oo + 0] = b; // b
|
||||
output[oo + 1] = g; // g
|
||||
output[oo + 2] = r; // r
|
||||
output[oo + 3] = a; // a
|
||||
io += 8;
|
||||
oo += 4;
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RFloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return RFloatToBGRA32(input, width, height, output);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RFloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int io = 0;
|
||||
int oo = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
byte r = ClampByte(ToSingle(input, io) * 255f);
|
||||
output[oo + 0] = 0; // b
|
||||
output[oo + 1] = 0; // g
|
||||
output[oo + 2] = r; // r
|
||||
output[oo + 3] = 255; // a
|
||||
io += 4;
|
||||
oo += 4;
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGFloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return RGFloatToBGRA32(input, width, height, output);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGFloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int io = 0;
|
||||
int oo = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
byte r = ClampByte(ToSingle(input, io + 0) * 255f);
|
||||
byte g = ClampByte(ToSingle(input, io + 4) * 255f);
|
||||
output[oo + 0] = 0; // b
|
||||
output[oo + 1] = g; // g
|
||||
output[oo + 2] = r; // r
|
||||
output[oo + 3] = 255; // a
|
||||
io += 8;
|
||||
oo += 4;
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGBAFloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return RGBAFloatToBGRA32(input, width, height, output);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGBAFloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int io = 0;
|
||||
int oo = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
byte r = ClampByte(ToSingle(input, io + 0) * 255f);
|
||||
byte g = ClampByte(ToSingle(input, io + 4) * 255f);
|
||||
byte b = ClampByte(ToSingle(input, io + 8) * 255f);
|
||||
byte a = ClampByte(ToSingle(input, io + 12) * 255f);
|
||||
output[oo + 0] = b; // b
|
||||
output[oo + 1] = g; // g
|
||||
output[oo + 2] = r; // r
|
||||
output[oo + 3] = a; // a
|
||||
io += 16;
|
||||
oo += 4;
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGB9e5FloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return RGB9e5FloatToBGRA32(input, width, height, output);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGB9e5FloatToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int io = 0;
|
||||
int oo = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
uint value = BinaryPrimitives.ReadUInt32LittleEndian(input.Slice(io, 4));
|
||||
double scale = Math.Pow(2, unchecked((int)(value >> 27) - 24));
|
||||
byte r = ClampByte((value >> 0 & 0x1FF) * scale * 255.0);
|
||||
byte g = ClampByte((value >> 9 & 0x1FF) * scale * 255.0);
|
||||
byte b = ClampByte((value >> 18 & 0x1FF) * scale * 255.0);
|
||||
output[oo + 0] = b; // b
|
||||
output[oo + 1] = g; // g
|
||||
output[oo + 2] = r; // r
|
||||
output[oo + 3] = 255; // a
|
||||
io += 4;
|
||||
oo += 4;
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RG32ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return RG32ToBGRA32(input, width, height, output);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RG32ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int io = 0;
|
||||
int oo = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
output[oo + 0] = 0; // b
|
||||
output[oo + 1] = input[io + 3]; // g
|
||||
output[oo + 2] = input[io + 1]; // r
|
||||
output[oo + 3] = 255; // a
|
||||
io += 4;
|
||||
oo += 4;
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGB48ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return RGB48ToBGRA32(input, width, height, output);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGB48ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int io = 0;
|
||||
int oo = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
output[oo + 0] = input[io + 5]; // b
|
||||
output[oo + 1] = input[io + 3]; // g
|
||||
output[oo + 2] = input[io + 1]; // r
|
||||
output[oo + 3] = 255; // a
|
||||
io += 6;
|
||||
oo += 4;
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGBA64ToBGRA32(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * 4];
|
||||
return RGBA64ToBGRA32(input, width, height, output);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int RGBA64ToBGRA32(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
int io = 0;
|
||||
int oo = 0;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
output[oo + 0] = input[io + 5]; // b
|
||||
output[oo + 1] = input[io + 3]; // g
|
||||
output[oo + 2] = input[io + 1]; // r
|
||||
output[oo + 3] = input[io + 7]; // a
|
||||
io += 8;
|
||||
oo += 4;
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int Convert<TSource, TSourceArg, TDestination, TDestinationArg>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TSourceArg : unmanaged
|
||||
where TSource : unmanaged, IColor<TSourceArg>
|
||||
where TDestinationArg : unmanaged
|
||||
where TDestination : unmanaged, IColor<TDestinationArg>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<TDestination>()];
|
||||
return Convert<TSource, TSourceArg, TDestination, TDestinationArg>(input, width, height, output);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int Convert<TSource, TSourceArg, TDestination, TDestinationArg>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TSourceArg : unmanaged
|
||||
where TSource : unmanaged, IColor<TSourceArg>
|
||||
where TDestinationArg : unmanaged
|
||||
where TDestination : unmanaged, IColor<TDestinationArg>
|
||||
{
|
||||
ReadOnlySpan<TSource> sourceSpan = MemoryMarshal.Cast<byte, TSource>(input).Slice(0, width * height);
|
||||
Span<TDestination> destinationSpan = MemoryMarshal.Cast<byte, TDestination>(output).Slice(0, width * height);
|
||||
Convert<TSource, TSourceArg, TDestination, TDestinationArg>(sourceSpan, destinationSpan);
|
||||
return width * height * Unsafe.SizeOf<TSource>();
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static void Convert<TSource, TSourceArg, TDestination, TDestinationArg>(ReadOnlySpan<TSource> sourceSpan, Span<TDestination> destinationSpan)
|
||||
where TSourceArg : unmanaged
|
||||
where TSource : unmanaged, IColor<TSourceArg>
|
||||
where TDestinationArg : unmanaged
|
||||
where TDestination : unmanaged, IColor<TDestinationArg>
|
||||
{
|
||||
for (int i = 0; i < sourceSpan.Length; i++)
|
||||
{
|
||||
TSource source = sourceSpan[i];
|
||||
TDestination destination = default;
|
||||
source.GetChannels(out TSourceArg r, out TSourceArg g, out TSourceArg b, out TSourceArg a);
|
||||
destination.SetConvertedChannels<TDestination, TDestinationArg, TSourceArg>(r, g, b, a);
|
||||
destinationSpan[i] = destination;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
private static float ToHalf(ReadOnlySpan<byte> input, int offset)
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
return (float)BinaryPrimitives.ReadHalfLittleEndian(input.Slice(offset, 2));
|
||||
#else
|
||||
ushort bits = BinaryPrimitives.ReadUInt16LittleEndian(input.Slice(offset, 2));
|
||||
return (float)Unsafe.As<ushort, Half>(ref bits);
|
||||
#endif
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
private static float ToSingle(ReadOnlySpan<byte> input, int offset)
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
return BinaryPrimitives.ReadSingleLittleEndian(input.Slice(offset, 4));
|
||||
#else
|
||||
uint bits = BinaryPrimitives.ReadUInt32LittleEndian(input.Slice(offset, 4));
|
||||
return Unsafe.As<uint, float>(ref bits);
|
||||
#endif
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
private static byte ClampByte(float x)
|
||||
{
|
||||
return byte.MaxValue < x ? byte.MaxValue : (x > byte.MinValue ? (byte)x : byte.MinValue);
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
private static byte ClampByte(double x)
|
||||
{
|
||||
return byte.MaxValue < x ? byte.MaxValue : (x > byte.MinValue ? (byte)x : byte.MinValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace AssetRipper.TextureDecoder
|
||||
{
|
||||
internal static class ThrowHelper
|
||||
{
|
||||
private const int BytesPerPixel = 4;
|
||||
|
||||
internal static void ThrowIfNotEnoughSpace(Span<byte> output, int width, int height)
|
||||
{
|
||||
if (output.Length < width * height * BytesPerPixel)
|
||||
{
|
||||
throw new InvalidOperationException("Not enough space");
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ThrowIfNotEnoughSpace(int spaceAvailable, int spaceRequired)
|
||||
{
|
||||
if (spaceAvailable < spaceRequired)
|
||||
{
|
||||
throw new InvalidOperationException($"Not enough space. {spaceRequired} is required, but only {spaceAvailable} is available");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Yuy2
|
||||
{
|
||||
public static class Yuy2Decoder
|
||||
{
|
||||
/// <summary>
|
||||
/// Decompress a YUY2 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer of size 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int DecompressYUY2(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
{
|
||||
output = new byte[width * height * sizeof(uint)];
|
||||
return DecompressYUY2(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a YUY2 image
|
||||
/// </summary>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least 4 * width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int DecompressYUY2(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output, width, height);
|
||||
|
||||
int p = 0;
|
||||
int o = 0;
|
||||
int halfWidth = width / 2;
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
for (int i = 0; i < halfWidth; ++i)
|
||||
{
|
||||
int y0 = input[p++];
|
||||
int u0 = input[p++];
|
||||
int y1 = input[p++];
|
||||
int v0 = input[p++];
|
||||
int c = y0 - 16;
|
||||
int d = u0 - 128;
|
||||
int e = v0 - 128;
|
||||
output[o++] = ClampByte((298 * c + 516 * d + 128) >> 8); // blue
|
||||
output[o++] = ClampByte((298 * c - 100 * d - 208 * e + 128) >> 8); // green
|
||||
output[o++] = ClampByte((298 * c + 409 * e + 128) >> 8); // red
|
||||
output[o++] = 255;
|
||||
c = y1 - 16;
|
||||
output[o++] = ClampByte((298 * c + 516 * d + 128) >> 8); // blue
|
||||
output[o++] = ClampByte((298 * c - 100 * d - 208 * e + 128) >> 8); // green
|
||||
output[o++] = ClampByte((298 * c + 409 * e + 128) >> 8); // red
|
||||
output[o++] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a YUY2 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutput"></typeparam>
|
||||
/// <typeparam name="TOutputArg"></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>
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int DecompressYUY2<TOutput, TOutputArg>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputArg : unmanaged
|
||||
where TOutput : unmanaged, IColor<TOutputArg>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutput>()];
|
||||
return DecompressYUY2<TOutput, TOutputArg>(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a YUY2 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutput"></typeparam>
|
||||
/// <typeparam name="TOutputArg"></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>
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int DecompressYUY2<TOutput, TOutputArg>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputArg : unmanaged
|
||||
where TOutput : unmanaged, IColor<TOutputArg>
|
||||
{
|
||||
return DecompressYUY2<TOutput, TOutputArg>(input, width, height, MemoryMarshal.Cast<byte, TOutput>(output));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a YUY2 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutput"></typeparam>
|
||||
/// <typeparam name="TOutputArg"></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.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
public static int DecompressYUY2<TOutput, TOutputArg>(ReadOnlySpan<byte> input, int width, int height, Span<TOutput> output)
|
||||
where TOutputArg : unmanaged
|
||||
where TOutput : unmanaged, IColor<TOutputArg>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
|
||||
int p = 0;
|
||||
int o = 0;
|
||||
int halfWidth = width / 2;
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
for (int i = 0; i < halfWidth; ++i)
|
||||
{
|
||||
int y0 = input[p++];
|
||||
int u0 = input[p++];
|
||||
int y1 = input[p++];
|
||||
int v0 = input[p++];
|
||||
int c = y0 - 16;
|
||||
int d = u0 - 128;
|
||||
int e = v0 - 128;
|
||||
byte b0 = ClampByte((298 * c + 516 * d + 128) >> 8); // blue
|
||||
byte g0 = ClampByte((298 * c - 100 * d - 208 * e + 128) >> 8); // green
|
||||
byte r0 = ClampByte((298 * c + 409 * e + 128) >> 8); // red
|
||||
output[o++].SetConvertedChannels<TOutput, TOutputArg, byte>(r0, g0, b0, byte.MaxValue);
|
||||
c = y1 - 16;
|
||||
byte b1 = ClampByte((298 * c + 516 * d + 128) >> 8); // blue
|
||||
byte g1 = ClampByte((298 * c - 100 * d - 208 * e + 128) >> 8); // green
|
||||
byte r1 = ClampByte((298 * c + 409 * e + 128) >> 8); // red
|
||||
output[o++].SetConvertedChannels<TOutput, TOutputArg, byte>(r1, g1, b1, byte.MaxValue);
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
[MethodImpl(OptimizationConstants.AggressiveInliningAndOptimization)]
|
||||
private static byte ClampByte(int x)
|
||||
{
|
||||
return (byte)(byte.MaxValue < x ? byte.MaxValue : (x > byte.MinValue ? x : byte.MinValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>netframework472;net6.0</TargetFrameworks>
|
||||
<LangVersion>Latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\TextureDecoder\TextureDecoder.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,70 +0,0 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AstcTest
|
||||
{
|
||||
public sealed class DirectBitmap : IDisposable
|
||||
{
|
||||
public DirectBitmap(int width, int height)
|
||||
{
|
||||
Width = width;
|
||||
Height = height;
|
||||
Bits = new byte[width * height * 4];
|
||||
m_bitsHandle = GCHandle.Alloc(Bits, GCHandleType.Pinned);
|
||||
Bitmap = new Bitmap(Width, Height, Stride, PixelFormat.Format32bppArgb, m_bitsHandle.AddrOfPinnedObject());
|
||||
}
|
||||
|
||||
~DirectBitmap()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void SetPixel(int x, int y, Color color)
|
||||
{
|
||||
int index = x + (y * Width);
|
||||
unchecked
|
||||
{
|
||||
uint value = (uint)color.ToArgb();
|
||||
Bits[index + 0] = (byte)(value >> 0);
|
||||
Bits[index + 1] = (byte)(value >> 8);
|
||||
Bits[index + 2] = (byte)(value >> 16);
|
||||
Bits[index + 3] = (byte)(value >> 24);
|
||||
}
|
||||
}
|
||||
|
||||
public Color GetPixel(int x, int y)
|
||||
{
|
||||
int index = x + (y * Width);
|
||||
uint col = BitConverter.ToUInt32(Bits, index);
|
||||
return Color.FromArgb(unchecked((int)col));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!m_disposed)
|
||||
{
|
||||
Bitmap.Dispose();
|
||||
m_bitsHandle.Free();
|
||||
m_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public int Height { get; }
|
||||
public int Width { get; }
|
||||
public int Stride => Width * 4;
|
||||
public Bitmap Bitmap { get; }
|
||||
public byte[] Bits { get; }
|
||||
public IntPtr BitsPtr => m_bitsHandle.AddrOfPinnedObject();
|
||||
|
||||
private readonly GCHandle m_bitsHandle;
|
||||
private bool m_disposed;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using System;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using TextureDecoder.Astc;
|
||||
|
||||
namespace AstcTest
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
if (args.Length < 4)
|
||||
{
|
||||
Console.WriteLine("Format: {path} {width} {height} {blockXSize} {blockYSize}");
|
||||
}
|
||||
else
|
||||
{
|
||||
string path = args[0];
|
||||
int width = int.Parse(args[1]);
|
||||
int height = int.Parse(args[2]);
|
||||
int blockXSize = int.Parse(args[3]);
|
||||
int blockYSize = int.Parse(args[4]);
|
||||
byte[] data = File.ReadAllBytes(path);
|
||||
|
||||
using (DirectBitmap bitmap = new DirectBitmap(width, height))
|
||||
{
|
||||
AstcDecoder.DecodeASTC(data, width, height, blockXSize, blockYSize, bitmap.Bits);
|
||||
|
||||
string dirPath = Path.GetDirectoryName(path);
|
||||
string name = Path.GetFileNameWithoutExtension(path);
|
||||
string newPath = Path.Combine(dirPath, name + "_decoded.png");
|
||||
bitmap.Bitmap.Save(newPath, ImageFormat.Png);
|
||||
}
|
||||
|
||||
Console.WriteLine("Finished");
|
||||
}
|
||||
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 171 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user