Owner report 2026-09-06: MossTank's shelf icon (0x06002C41) drew with a white ring. DAT icon art reserves pure-white-opaque pixels as the recolor key that retail IconData::RenderIcons (0x0058d180) replaces per pixel through SurfaceWindow::ReplaceColor (0x004415b0) from the effect tile — the solid-black 0x21 tile when there are no effects. The inventory already does this through IconComposer; the plugin did sink (markup <icon did>, <button icon>, <list icons> and the shelf button) blitted the art raw. RetailMarkupIconResolver.ResolveDid now hands out IconComposer.GetKeyedIcon — the drag-icon composite (base art + effects==0 recolor, no overlay, no underlay), sharing that cache — so did icons look like a mundane inventory item does. The resolver no longer needs a TextureCache. KeyedIconInstalledDatTests pins both halves against the real DAT: the raw art carries the key, the composite carries none, and ResolveDid returns exactly the keyed composite. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
74 lines
2.9 KiB
C#
74 lines
2.9 KiB
C#
using AcDream.App.Rendering;
|
|
using AcDream.App.Tests.Rendering;
|
|
using AcDream.App.Tests.Rendering.Gpu;
|
|
using AcDream.App.UI;
|
|
using AcDream.Content;
|
|
using AcDream.Core.Items;
|
|
using DatReaderWriter;
|
|
using DatReaderWriter.Options;
|
|
using Xunit;
|
|
|
|
namespace AcDream.App.Tests.UI;
|
|
|
|
/// <summary>
|
|
/// Owner report 2026-09-06: the MossTank shelf icon (<c>0x06002C41</c>) drew
|
|
/// with a white ring. DAT icon art reserves pure-white-opaque pixels as the
|
|
/// "recolor me" key that retail <c>IconData::RenderIcons</c> replaces per pixel
|
|
/// through <c>SurfaceWindow::ReplaceColor</c> (effects==0 → the solid-black
|
|
/// 0x21 tile); blitting the art raw leaves the key visible. The plugin
|
|
/// <c>did</c> sink (<see cref="RetailMarkupIconResolver.ResolveDid"/>, also the
|
|
/// shelf button's path) therefore goes through
|
|
/// <see cref="IconComposer.GetKeyedIcon"/>, the drag-icon composite with no
|
|
/// overlay and no effects. This pins both halves: the raw art really does
|
|
/// carry the key (so the test is not vacuous), and the composed result has
|
|
/// none of it left.
|
|
/// </summary>
|
|
[Trait("Lane", "InstalledDat")]
|
|
public sealed class KeyedIconInstalledDatTests
|
|
{
|
|
private const uint MossTankShelfIconId = 0x06002C41u;
|
|
|
|
[Fact]
|
|
public void KeyedIcon_ReplacesTheArtsPureWhiteKey_AndResolveDidUsesIt()
|
|
{
|
|
string? datDir = InstalledDatTestPath.Resolve();
|
|
if (datDir is null)
|
|
{
|
|
Assert.Fail(
|
|
"Lane=InstalledDat requires an installed retail DAT directory; see docs/release-gate.md.");
|
|
return;
|
|
}
|
|
|
|
using var dats = new DatCollection(datDir, DatAccessType.Read);
|
|
using var adapter = new DatCollectionAdapter(dats);
|
|
var device = new RecordingGpuDevice();
|
|
using var cache = new TextureCache(device, adapter);
|
|
var icons = new IconComposer(adapter, cache);
|
|
|
|
Assert.True(icons.TryDecodeRaw(MossTankShelfIconId, out byte[] raw, out int rw, out int rh));
|
|
Assert.True(
|
|
CountPureWhite(raw) > 0,
|
|
"the raw art must carry pure-white keyed pixels, or this pin proves nothing");
|
|
|
|
Assert.True(icons.TryGetKeyedIconRgba(MossTankShelfIconId, out byte[] keyed, out int kw, out int kh));
|
|
Assert.Equal((rw, rh), (kw, kh));
|
|
Assert.Equal(0, CountPureWhite(keyed));
|
|
|
|
// The markup/shelf did sink must hand out exactly the keyed composite.
|
|
var resolver = new RetailMarkupIconResolver(adapter, icons, new ClientObjectTable());
|
|
(uint tex, int w, int h) = resolver.ResolveDid(MossTankShelfIconId);
|
|
Assert.Equal(icons.GetKeyedIcon(MossTankShelfIconId), (tex, w, h));
|
|
Assert.NotEqual(0u, tex);
|
|
}
|
|
|
|
private static int CountPureWhite(byte[] rgba)
|
|
{
|
|
int count = 0;
|
|
for (int i = 0; i + 3 < rgba.Length; i += 4)
|
|
{
|
|
if (rgba[i] == 255 && rgba[i + 1] == 255 && rgba[i + 2] == 255 && rgba[i + 3] == 255)
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
}
|