feat(D.5.1): IconComposer — CPU alpha-over icon composite + cache
Adds IconComposer (AcDream.App.UI) which mirrors retail IconData::RenderIcons (decomp 407524): decodes each RenderSurface layer directly via SurfaceDecoder, composites them bottom-to-top with Porter-Duff alpha-over, and uploads the result to a GL texture via TextureCache. Composited handles are keyed by the (iconId, underlayId, overlayId) tuple so each unique combo is uploaded once. Adds a public TextureCache.UploadRgba8(byte[], int, int, bool) wrapper — a thin shell around the existing private overload — so IconComposer can upload its CPU-side composite without duplicating any GL state logic. Pure Compose() path is covered by 2 unit tests (opaque top wins; transparent top preserves bottom). Dat-decode + GL-upload exercised by the visual gate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6c485c2f06
commit
6e82807863
3 changed files with 129 additions and 0 deletions
36
tests/AcDream.App.Tests/UI/IconComposerTests.cs
Normal file
36
tests/AcDream.App.Tests/UI/IconComposerTests.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
using AcDream.App.UI;
|
||||
|
||||
namespace AcDream.App.Tests.UI;
|
||||
|
||||
public class IconComposerTests
|
||||
{
|
||||
private static byte[] Solid(int w, int h, byte r, byte g, byte b, byte a)
|
||||
{
|
||||
var px = new byte[w * h * 4];
|
||||
for (int i = 0; i < w * h; i++) { px[i*4]=r; px[i*4+1]=g; px[i*4+2]=b; px[i*4+3]=a; }
|
||||
return px;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Compose_alphaOver_topOpaqueLayerWins()
|
||||
{
|
||||
var bottom = (Solid(2, 2, 255, 0, 0, 255), 2, 2); // red, opaque
|
||||
var top = (Solid(2, 2, 0, 0, 255, 255), 2, 2); // blue, opaque
|
||||
var (rgba, w, h) = IconComposer.Compose(new[] { bottom, top });
|
||||
Assert.Equal(2, w); Assert.Equal(2, h);
|
||||
Assert.Equal(0, rgba[0]); // R
|
||||
Assert.Equal(0, rgba[1]); // G
|
||||
Assert.Equal(255, rgba[2]); // B — top layer won
|
||||
Assert.Equal(255, rgba[3]); // A
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Compose_alphaOver_transparentTopKeepsBottom()
|
||||
{
|
||||
var bottom = (Solid(1, 1, 255, 0, 0, 255), 1, 1);
|
||||
var top = (Solid(1, 1, 0, 0, 255, 0), 1, 1); // fully transparent blue
|
||||
var (rgba, _, _) = IconComposer.Compose(new[] { bottom, top });
|
||||
Assert.Equal(255, rgba[0]); // bottom red preserved
|
||||
Assert.Equal(0, rgba[2]);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue