Ported Bc code

This commit is contained in:
Jeremy Pritts committed 2022-07-16 15:49:16 -04:00
1 parent 8458eb5866
commit be63ebdb23
17 files changed
+2896 -1

No files matched your search

@@ -5,6 +5,8 @@
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PublishSingleFile>true</PublishSingleFile>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
<ItemGroup>
@@ -1,5 +1,6 @@
using AssetRipper.TextureDecoder.Astc;
using AssetRipper.TextureDecoder.Atc;
using AssetRipper.TextureDecoder.Bc;
using AssetRipper.TextureDecoder.Dxt;
using AssetRipper.TextureDecoder.Etc;
using AssetRipper.TextureDecoder.Pvrtc;
@@ -35,6 +36,9 @@ internal static class Program
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;
@@ -104,6 +108,47 @@ internal static class Program
}
}
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");
+189
View File
@@ -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");
});
}
}
}
+249
View File
@@ -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 };
}
+929
View File
@@ -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;
}
+191
View File
@@ -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;
}
+42
View File
@@ -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
*/
+15
View File
@@ -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;
}
+52
View File
@@ -0,0 +1,52 @@
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2022 Sergii Kudlai
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - The Unlicense
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>
+2
View File
@@ -4,4 +4,6 @@ Managed library for decoding textures
# Credits
Forked code from [bcdec](https://github.com/iOrange/bcdec) licensed under the [MIT license](Licenses/bcdec.md)
Forked code from [uTinyRipper](https://github.com/mafaca/UtinyRipper) licensed under the [MIT license](Licenses/uTinyRipper.md)
Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
 (� (� (� (� (� (� (� (� )� (� )� )� )�!)� )� )�!)�!)�!)�!)�!)�!)�!*�!)�!)�!)�!*�!*�!*�!*�!*�!*�!*�"*�!*�!*�!*�!*�!*�"*�"*�"+�"+�"+�"+�"+�"+�"+�"+�"+�"+�"+�"+�#,�"+�#,�#,�#,�#,�#,�#,�#,�#,�#,�#,�#,�#,�$-�$-�#-�$-�$-�$-�$-�$-�$-�$-�$-�$-�%.�$.�$.�%.�%.�%.�%.�%.�%/�%.�%/�%/�%/�%/�%/�%/�&/�&/�%/�&0�&/�&/�&0�&0�&0�&0�&0�&0�&0�&0�&1�&1�&1�'1�'1�'1�'1�'1�'1�'2�'2�(2�(2�'2�'2�(2�'2�(3�(3�(3�(3�(3�(3�(3�(3�(3�(3�)4�)4�)4�)4�)4�)4�)4�)4�)4�)4�)5�)5�)5�)5�)5�*5�*5�*5�*5�*6�*6�*6�*6�*6�*6�*6�+6�+6�+7�+6�+7�+7�+7�+7�+7�+7�+7�+7�,7�,8�+7�,8�,8�,7�,8�,8�,8�,8�,8�,8�,8�,8�,9�,9�-9�-9�-9�-9�-9�-9�-9�-9�-:�-9�-:�-:�-:�-:�-:�-:�-:�-:�-:�.:�.:�.:�.:�.:�.;�.;�.;�.;�.;�.;�.;�.;�.;�.;�.;�.;�.;�.;�.;�/;�.;�.;�.;�/<�.<�/<�/<�/<�.<�/<�/<�/<�.<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�.<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�.<�/<�/<�/<�/<�/<�/<�.<�.;�.;�.;�.;�/<�.;�.;�.;�.;�.;�.;�.;�.;�.;�.;�.;�.;�.;�.;�.:�.;�.;�.:�.:�-:�-:�-:�-:�-:�-:�-:�-:�-:�-:�-:�-:�-:�-9�-9�-9�-9�-9�-9�-9�,9�,9�,8�,8�,8�,8�,8�,8�,8�,8�,8�,7�,8�,8�,8�,8�,7�+7�+7�+7�+7�+7�+7�+7�+7�+6�+7�+7�*6�*6�+6�*6�*6�*6�*6�*6�*5�*5�*5�*5�)5�)5�)5�)5�)5�)5�)5�)5�)4�)4�)4�)4�)4�)4�)4�(3�(3�(3�(3�(3�(3�(3�(3�(2�(2�(3�'2�(2�(2�(2�'2�'1�'2�'1�'1�'1�'1�'1�'1�'1�'1�'1�&0�&0�&0�&0�&0�&0�&0�&0�&0�&/�&/�%/�&/�%/�%/�%/�%/�%/�%/�%.�%/�%/�%.�%.�%.�%.�%.�$.�$.�%.�$.�$-�$-�$-�$-�$-�$-�$-�$-�$-�$-�$,�$-�$,�#,�#,�#,�#,�#,�#,�#,�#,�#,�",�#,�"+�#+�"+�"+�"+�"+�"+�"+�"+�"+�"+�"+�"+�"*�"*�"+�!*�"*�!*�!*�"*�!*�!*�!*�!*�!*�!*�!*�!)�!)�!)�!)�!)�!)�!)�!)�!)�!)�!)� )� )� )� (� )�!(� (� (� (� (� (� (�!(� (� (� (� (� (� (� (� (� (� (� (�!(� )�!)�!)� )� )� )�!)�!)�!)�!)�!)�!)�!)�!)�!*�!*�!*�!*�!*�!*�!*�!*�!*�!*�!*�!*�"*�!*�!*�"+�"*�"+�"*�"*�"+�"+�"+�"+�"+�"+�"+�"+�#,�"+�#,�#,�#,�#,�#,�#,�#,�#,�#,�#,�#,�#,�#,�$-�$-�$-�$-�$-�$-�$-�$-�$-�$-�$.�$-�%.�$.�%.�%.�%.�%.�%.�%.�%.�%/�%/�%/�%/�%/�%/�%/�&/�&/�&/�&/�&/�&/�&0�&0�&0�&0�&0�&0�&0�&0�'1�'1�'1�'1�'1�'1�'1�'1�'2�'2�'2�'2�'2�'2�(2�'2�(3�(3�(2�(3�(3�(3�(3�(3�(3�(3�)3�)4�)4�)4�)4�)4�)4�)4�)5�)5�)5�)5�)5�)5�)5�)5�*5�*5�*5�*5�*6�*6�*6�*6�+6�*6�+6�+6�+6�+7�+6�+7�+7�+7�+7�+7�+7�+7�+7�,7�,8�,8�,8�,8�,8�,7�,8�,8�,8�,8�,8�,9�,8�,8�-9�-9�-9�-9�-9�-9�-9�-9�-9�-:�-9�-:�-:�-:�-:�-:�-:�.:�.:�.:�.:�.:�.:�.:�.;�.;�.;�.;�.:�.;�.;�.;�.;�.;�.;�.;�.;�.;�.;�.<�.;�.;�.;�.;�.;�/<�.<�.;�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�.<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�/<�.<�.<�.;�.;�.;�.;�.;�.;�.;�.;�.;�.;�.;�.;�.;�.;�.;�.;�.;�.;�.:�.;�.;�.:�.:�-:�-:�-:�-:�-:�-:�-:�-:�-:�-:�-:�-:�-9�-9�-9�-9�-9�-9�-9�,9�,9�,8�,8�,8�,9�,8�,8�,8�,8�,8�,8�,7�,7�,8�,8�,7�,7�+7�+7�+7�+7�+7�+7�+7�+7�+7�+6�+7�+7�+6�*6�*6�*6�*6�*6�*6�*6�*5�*5�*5�*5�)5�)5�)5�)5�)5�)4�)4�)4�)4�)4�)4�)4�)4�)4�)3�(3�(3�(3�(3�(3�(3�(3�(3�(2�(2�(2�'2�'2�'2�'2�'2�'2�'2�'1�'1�'1�'1�'1�&0�'1�'1�&1�'0�&0�&0�&0�&0�&0�&0�&/�&/�&/�%/�&/�&/�%/�%/�%/�%/�%/�%.�%.�%/�%.�%.�%.�%.�%.�$.�%.�$.�$-�$.�$.�$-�$-�$-�$-�$-�#-�$-�$-�$-�#-�#,�#,�#,�#,�#,�#,�#,�#,�#,�#,�#,�",�#,�#+�",�"+�"+�"+�"+�"+�"+�"+�"+�"+�"+�"*�!*�!*�!*�!*�!*�!*�!*�!*�!*�!*�!*�!*�!*�!)�!)�!)� )�!)�!)�!)�!)�!)�!)�!)�!)�!)�!)�!)�!)� )�!(� (� )� (� (� (� (� (� (� (� (� (� (� (� (� (� (� (� (�!)� )�!)� )� )�!)�!)�!)�!)�!)�!)�!)�!)�!*�!)�!)�!*�!*�!*�!*�!*�!*�!*�!*�!*�!*�!*�!*�!*�!*�"*�"*�"+�"+�"+�"+�"+�"+�"+�"+�"+�"+�"+�"+�"+�",�#,�#,�#,�#,�#,�#,�#,�#,�#,�#,�#,�#,�$-�$-�$-�$-�$-�$-�$-�$-�$-�$-�$.�$-�$.�$.�$.�$.�%.�%.�%.�%.�%/�%/�%/�%/�%/�%/�%/�&/�&/�&/�&/�&/�&/�&0�&0�&0�&0�&0�&0�&0�&0�&0�'1�&1�'1�'1�'1�'1�'1�'2�'2�'2�'2�(2�'2�'2�'2�(3�(2�(2�(3�(3�(3�(3�(3�(3�(3�(3�)4�)4�)4�)4�)4�)4�)4�)4�)4�)5�)4�)4�)5�)5�*5�*5�)5�*5�*5�*5�*6�*6�*6�*6�*6�*6�*6�+6�*6�+7�+7�+7�+7�+7�+7�+7�+7�+7�+7�,7�,8�,8�,8�,8�,8�,8�,8�,8�,8�,8�,9�-9�,8�,8�,9�,9�,9�-9�-9�-9�-9�-9�-9�-9�-9�-:�-:�-:�-:�-:�-:�-:�-:�-:�-:�.:�.:�.:�.;�.;�.:�.:�.;�.;�.;�.;�.;�.;�.;�.;�.Line truncated
+2 -1
View File
@@ -17,6 +17,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Workflows", "Workflows", "{
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Licenses", "Licenses", "{0BFF6817-43C8-43EE-8EE8-1A03B7ECEA1A}"
ProjectSection(SolutionItems) = preProject
Licenses\bcdec.md = Licenses\bcdec.md
Licenses\uTinyRipper.md = Licenses\uTinyRipper.md
EndProjectSection
EndProject
@@ -26,7 +27,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetRipper.TextureDecoder"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetRipper.TextureDecoder.TestGenerator", "AssetRipper.TextureDecoder.TestGenerator\AssetRipper.TextureDecoder.TestGenerator.csproj", "{0074908C-35A8-4C71-8F74-A83A4095ACEA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetRipper.TextureDecoder.ConsoleApp", "AssetRipper.TextureDecoder.ConsoleApp\AssetRipper.TextureDecoder.ConsoleApp.csproj", "{FFC8EEF8-8BF1-4B65-ADDA-A949EDDF4CB4}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetRipper.TextureDecoder.ConsoleApp", "AssetRipper.TextureDecoder.ConsoleApp\AssetRipper.TextureDecoder.ConsoleApp.csproj", "{FFC8EEF8-8BF1-4B65-ADDA-A949EDDF4CB4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetRipper.TextureDecoder.Benchmarks", "AssetRipper.TextureDecoder.Benchmarks\AssetRipper.TextureDecoder.Benchmarks.csproj", "{F85804F4-4864-412E-BDC6-8B6E2B5646A7}"
EndProject