using AcDream.Core.Textures; using DatReaderWriter.DBObjs; using DatReaderWriter.Enums; using DatReaderWriter.Types; namespace AcDream.Core.Tests.Textures; public class SurfaceDecoderTests { [Fact] public void Decode_A8R8G8B8_ConvertsToRgba8() { // Source format is B, G, R, A in memory (little-endian ARGB). // One 2x2 image: red, green, blue, white pixels. var src = new byte[] { 0x00, 0x00, 0xFF, 0xFF, // red (B=0, G=0, R=255, A=255) 0x00, 0xFF, 0x00, 0xFF, // green 0xFF, 0x00, 0x00, 0xFF, // blue 0xFF, 0xFF, 0xFF, 0xFF, // white }; var rs = new RenderSurface { Width = 2, Height = 2, Format = PixelFormat.PFID_A8R8G8B8, SourceData = src, }; var decoded = SurfaceDecoder.DecodeRenderSurface(rs); Assert.Equal(2, decoded.Width); Assert.Equal(2, decoded.Height); Assert.Equal(16, decoded.Rgba8.Length); // 2*2*4 // red pixel, in RGBA: 255, 0, 0, 255 Assert.Equal(0xFF, decoded.Rgba8[0]); Assert.Equal(0x00, decoded.Rgba8[1]); Assert.Equal(0x00, decoded.Rgba8[2]); Assert.Equal(0xFF, decoded.Rgba8[3]); } [Fact] public void Decode_UnsupportedFormat_ReturnsMagenta() { var rs = new RenderSurface { Width = 4, Height = 4, Format = PixelFormat.PFID_INDEX16, // not implemented path SourceData = new byte[32], }; var decoded = SurfaceDecoder.DecodeRenderSurface(rs); Assert.Same(DecodedTexture.Magenta, decoded); } [Fact] public void Decode_NullSourceData_ReturnsMagenta() { var rs = new RenderSurface { Width = 4, Height = 4, Format = PixelFormat.PFID_A8R8G8B8, SourceData = null!, }; var decoded = SurfaceDecoder.DecodeRenderSurface(rs); Assert.Same(DecodedTexture.Magenta, decoded); } [Fact] public void Decode_TruncatedA8R8G8B8_ReturnsMagenta() { // Buffer too small for width*height*4. var rs = new RenderSurface { Width = 2, Height = 2, Format = PixelFormat.PFID_A8R8G8B8, SourceData = new byte[8], // should be 16 }; var decoded = SurfaceDecoder.DecodeRenderSurface(rs); Assert.Same(DecodedTexture.Magenta, decoded); } [Fact] public void DecodeSolidColor_Opaque_PreservesAlpha() { var color = new ColorARGB { Alpha = 0xFF, Red = 0x11, Green = 0x22, Blue = 0x33 }; var decoded = SurfaceDecoder.DecodeSolidColor(color, translucency: 0f); Assert.Equal(1, decoded.Width); Assert.Equal(1, decoded.Height); Assert.Equal(new byte[] { 0x11, 0x22, 0x33, 0xFF }, decoded.Rgba8); } [Fact] public void DecodeSolidColor_FullyTranslucent_AlphaGoesToZero() { // Surfaces marked Base1Solid + Translucent with Translucency=1.0 are // AC's convention for "invisible placeholder surfaces" — the engine renders // them as nothing. Alpha must go to 0 so the mesh shader's discard rule // makes them invisible. var color = new ColorARGB { Alpha = 0xFF, Red = 0xC8, Green = 0xC8, Blue = 0xC8 }; var decoded = SurfaceDecoder.DecodeSolidColor(color, translucency: 1f); Assert.Equal(0, decoded.Rgba8[3]); // alpha must be zero } [Fact] public void DecodeIndex16_ClipMap_ZerosAlphaForLowIndices() { // Build a 4x1 INDEX16 surface with indices 0, 1, 7, 8. // On a clipmap surface, indices 0..7 should be fully transparent and // index 8 should render with its palette color. var rs = new RenderSurface { Width = 4, Height = 1, Format = PixelFormat.PFID_INDEX16, SourceData = new byte[] { 0x00, 0x00, // index 0 0x01, 0x00, // index 1 0x07, 0x00, // index 7 0x08, 0x00, // index 8 }, }; var palette = new Palette(); for (int i = 0; i < 16; i++) palette.Colors.Add(new ColorARGB { Alpha = 0xFF, Red = 0xAA, Green = 0xBB, Blue = 0xCC }); var decoded = SurfaceDecoder.DecodeRenderSurface(rs, palette, isClipMap: true); // Pixels 0, 1, 2 (indices 0, 1, 7) should be fully transparent. Assert.Equal(0, decoded.Rgba8[3]); // pixel 0 alpha Assert.Equal(0, decoded.Rgba8[7]); // pixel 1 alpha Assert.Equal(0, decoded.Rgba8[11]); // pixel 2 alpha // Pixel 3 (index 8) should have the palette alpha. Assert.Equal(0xFF, decoded.Rgba8[15]); Assert.Equal(0xAA, decoded.Rgba8[12]); } }