test(N.3): update SurfaceDecoderTests to match isAdditive split

Decode_A8_ExpandsSingleByteToRgbaWithAlphaInAllChannels renamed to
Decode_A8_NonAdditive_ProducesWhitePlusAlpha with updated expectations
(R=G=B=255, A=val) matching the new default isAdditive:false WB semantics.

Decode_CustomLscapeAlpha_TreatedIdenticallyToA8 updated to the same
non-additive expectation (255,255,255,val).

New test Decode_A8_Additive_ReplicatesByteToAllChannels documents the
isAdditive:true path (R=G=B=A=val) used by TerrainAtlas alpha maps.

8 pre-existing failures unchanged. 883 pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-08 11:34:32 +02:00
parent 0a67254c5e
commit d467c4cf24

View file

@ -56,12 +56,10 @@ public class SurfaceDecoderTests
} }
[Fact] [Fact]
public void Decode_A8_ExpandsSingleByteToRgbaWithAlphaInAllChannels() public void Decode_A8_NonAdditive_ProducesWhitePlusAlpha()
{ {
// PFID_A8 is single-byte-per-pixel alpha. AC terrain blending alpha maps // Default (isAdditive: false) = WB FillA8 semantics: R=G=B=255, A=val.
// are stored this way. WorldBuilder's GetExpandedAlphaTexture replicates // Used for non-additive entity surfaces where A8 is a pure alpha channel.
// the byte into all four RGBA channels so fragment shaders can read the
// blend value from any channel (convention: the alpha channel).
var src = new byte[] { 0x00, 0x40, 0x80, 0xFF }; // 2x2 image var src = new byte[] { 0x00, 0x40, 0x80, 0xFF }; // 2x2 image
var rs = new RenderSurface var rs = new RenderSurface
{ {
@ -76,7 +74,34 @@ public class SurfaceDecoderTests
Assert.Equal(2, decoded.Width); Assert.Equal(2, decoded.Width);
Assert.Equal(2, decoded.Height); Assert.Equal(2, decoded.Height);
Assert.Equal(16, decoded.Rgba8.Length); Assert.Equal(16, decoded.Rgba8.Length);
// Each input byte expands to (b, b, b, b) in RGBA output // Each input byte expands to (255, 255, 255, val) — white with varying alpha
Assert.Equal(new byte[]
{
255, 255, 255, 0x00,
255, 255, 255, 0x40,
255, 255, 255, 0x80,
255, 255, 255, 0xFF,
}, decoded.Rgba8);
}
[Fact]
public void Decode_A8_Additive_ReplicatesByteToAllChannels()
{
// isAdditive=true = WB FillA8Additive semantics: R=G=B=A=val.
// Used for terrain blending alpha masks (TerrainAtlas always passes isAdditive:true).
var src = new byte[] { 0x00, 0x40, 0x80, 0xFF }; // 2x2 image
var rs = new RenderSurface
{
Width = 2,
Height = 2,
Format = PixelFormat.PFID_A8,
SourceData = src,
};
var decoded = SurfaceDecoder.DecodeRenderSurface(rs, palette: null, isClipMap: false, isAdditive: true);
Assert.Equal(16, decoded.Rgba8.Length);
// Each input byte fans out to all four channels
Assert.Equal(new byte[] Assert.Equal(new byte[]
{ {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -92,7 +117,7 @@ public class SurfaceDecoderTests
// PFID_CUSTOM_LSCAPE_ALPHA (0xF4) is AC's custom format for terrain // PFID_CUSTOM_LSCAPE_ALPHA (0xF4) is AC's custom format for terrain
// blending alpha maps. Pixel layout is identical to PFID_A8 — one // blending alpha maps. Pixel layout is identical to PFID_A8 — one
// byte of alpha per pixel — so the decoder routes both through the // byte of alpha per pixel — so the decoder routes both through the
// same DecodeA8 implementation. // same DecodeA8 implementation. Default (isAdditive:false) → R=G=B=255, A=val.
var src = new byte[] { 0x10, 0x20, 0x30, 0x40 }; // 2x2 var src = new byte[] { 0x10, 0x20, 0x30, 0x40 }; // 2x2
var rs = new RenderSurface var rs = new RenderSurface
{ {
@ -107,10 +132,10 @@ public class SurfaceDecoderTests
Assert.Equal(16, decoded.Rgba8.Length); Assert.Equal(16, decoded.Rgba8.Length);
Assert.Equal(new byte[] Assert.Equal(new byte[]
{ {
0x10, 0x10, 0x10, 0x10, 255, 255, 255, 0x10,
0x20, 0x20, 0x20, 0x20, 255, 255, 255, 0x20,
0x30, 0x30, 0x30, 0x30, 255, 255, 255, 0x30,
0x40, 0x40, 0x40, 0x40, 255, 255, 255, 0x40,
}, decoded.Rgba8); }, decoded.Rgba8);
} }