using AssetRipper.TextureDecoder.Etc; using AssetRipper.TextureDecoder.Rgb; using AssetRipper.TextureDecoder.Rgb.Formats; namespace AssetRipper.TextureDecoder.Tests; public sealed class EtcTests { private static DecodingDelegate ETCDelegate { get; } = EtcDecoder.DecompressETC, byte>; private static DecodingDelegate ETC2Delegate { get; } = EtcDecoder.DecompressETC2, byte>; private static DecodingDelegate ETC2A1Delegate { get; } = EtcDecoder.DecompressETC2A1, byte>; private static DecodingDelegate ETC2A8Delegate { get; } = EtcDecoder.DecompressETC2A8, byte>; private static DecodingDelegate EACRSignedDelegate { get; } = EtcDecoder.DecompressEACRSigned, byte>; private static DecodingDelegate EACRUnsignedDelegate { get; } = EtcDecoder.DecompressEACRUnsigned, byte>; private static DecodingDelegate EACRGSignedDelegate { get; } = EtcDecoder.DecompressEACRGSigned, byte>; private static DecodingDelegate EACRGUnsignedDelegate { get; } = EtcDecoder.DecompressEACRGUnsigned, byte>; [Test] public void DecompressETCTest() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.etc", ETCDelegate); [Test] public void DecompressETC2Test() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.etc2", ETC2Delegate); [Test] public void DecompressETC2A1Test() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.etc2a1", ETC2A1Delegate); [Test] public void DecompressETC2A8Test() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.etc2a8", ETC2A8Delegate); [Test] public void DecompressEACRSignedTest() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.eac_r", EACRSignedDelegate); [Test] public void DecompressEACRUnsignedTest() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.eac_r", EACRUnsignedDelegate); [Test] public void DecompressEACRGSignedTest() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.eac_rg", EACRGSignedDelegate); [Test] public void DecompressEACRGUnsignedTest() => AssertCorrectByteCountReadFor256SquareWithMips(TestFileFolders.EtcTestFiles + "test.eac_rg", EACRGUnsignedDelegate); [Test] public void CorrectnessETCTest() => AssertCorrectDecompression>(ETCDelegate, .05, 2.1); [Test] public void CorrectnessETC2Test() => AssertCorrectDecompression>(ETC2Delegate, .15, 1.6); [Test] public void CorrectnessETC2A1Test() => AssertCorrectDecompression(ETC2A1Delegate, .15, 1.7); [Test] public void CorrectnessETC2A8Test() => AssertCorrectDecompression(ETC2A8Delegate, .15, 1.6); [Test] public void CorrectnessEACRUnsignedTest() => AssertCorrectDecompression>(EACRUnsignedDelegate, .05, .5); [Test] public void CorrectnessEACRGUnsignedTest() => AssertCorrectDecompression>(EACRGUnsignedDelegate, .1, .6); private delegate int DecodingDelegate(ReadOnlySpan data, int width, int height, out byte[] decompressedData); private static void AssertCorrectByteCountReadFor256SquareWithMips(string path, DecodingDelegate decoder) { byte[] data = File.ReadAllBytes(path); int totalBytesRead = 0; foreach (int size in new int[] { 256, 128, 64, 32, 16, 8, 4, 2, 1 }) //mip maps { int bytesRead = decoder.Invoke(new ReadOnlySpan(data, totalBytesRead, data.Length - totalBytesRead), size, size, out _); totalBytesRead += bytesRead; } Assert.That(totalBytesRead, Is.EqualTo(data.Length)); } private static void AssertCorrectDecompression(DecodingDelegate decoder, double maxMeanDeviation, double maxStandardDeviation) where T : ITexture { AssertCorrectDecompression>(decoder, maxMeanDeviation, maxStandardDeviation); } private static void AssertCorrectDecompression(DecodingDelegate decoder, double maxMeanDeviation, double maxStandardDeviation) where TTexture : ITexture where TColor : unmanaged, IColor { byte[] data = TTexture.Data; int bytesRead = decoder.Invoke(new ReadOnlySpan(data), TTexture.Width, TTexture.Height, out byte[] decompressedData); if (!TTexture.Mips) { Assert.That(bytesRead, Is.EqualTo(data.Length)); } byte[] comparisonData = AndroidTextures.Logo_00.Data; //Remove unused color channels if (typeof(TColor) != typeof(ColorBGRA) && typeof(TColor) != typeof(ColorRGBA)) { Span> pixels = MemoryMarshal.Cast>((Span)comparisonData); for (int i = 0; i < pixels.Length; i++) { pixels[i] = pixels[i].Convert, byte, TColor, byte>().Convert, byte>(); } } ByteArrayDeviation.AssertMinimalDeviation(decompressedData, comparisonData, maxMeanDeviation, maxStandardDeviation); } }