# Texture colour interpretation — `k_8_8_8_8` (32bpp UI/HUD textures)

- **Confidence:** mixed — see per-claim tags below.
- **Parser in:** `sylpheed-formats/src/t8ad.rs`, `src/texture.rs` (XPR2)
- **Applies to:** T8aD 2D UI textures, XPR2 32bpp surfaces — the "colours look a bit odd" concern.
- **Related:** [[reborn-dynamic-re-plan]], INDEX rows T8aD / XPR2.

## What this resolves
Whether our decoded 32bpp textures use the right **sRGB-vs-linear** interpretation and the right
**channel order**. Ground truth = Canary's Vulkan texture cache, which is the code that actually
produces correct on-screen colour.

## Findings

### ✅ CONFIRMED — plain `k_8_8_8_8` is LINEAR (UNORM), never sRGB
Canary's host-format table maps plain `k_8_8_8_8` → `VK_FORMAT_R8G8B8A8_UNORM` with a straight
32-bit copy load shader (`kLoadShaderIndex32bpb`) and an **identity** format swizzle
(`XE_GPU_TEXTURE_SWIZZLE_RGBA`). There is **no** `..._SRGB` host format anywhere in the table;
gamma is a separate explicit path (`k_8_8_8_8_GAMMA_EDRAM` only). Therefore a plain 32bpp texture
is sampled as **raw UNORM bytes — apply no sRGB/gamma decode.**
→ *Implication:* if the viewer/engine applies an sRGB→linear (or linear→sRGB) step to these, that is
wrong. Show the bytes as-is (raw RGBA8), gamma only where the game explicitly requests it.

### ✅ CONFIRMED — on-disk order is **ARGB** (alpha first); `[a,r,g,b]→[r,g,b,a]` is correct
Settled by two independent lines that agree, without an emulator run:
1. **Measured on the disc data** — across 789 real T8aD textures (GP_HANGAR_ARSENAL, GP_MAIN_GAME_E,
   DefTables), the byte position that is most-often exactly `0xFF` (the opaque-alpha signature of UI
   art) is **byte 0 in 767/789 (97%)** — tf1 385/0, tf2 382/12. So texel byte 0 = alpha ⇒ on-disk
   layout is **A,R,G,B**. (An earlier bimodality test tied byte0/byte3 only because colour channels
   are also `0x00`-heavy from black backgrounds; the `==0xFF` discriminator isolates alpha cleanly.)
2. **Cross-check vs our Canary-validated renderer** — `xenia-rs`'s `decode_k8888_tiled` (the path
   behind the user-confirmed M1 splash / M2 video colours) nets memory-`ARGB` → endian-swap →
   `swap(0,2)` = `[A,R,G,B]→[R,G,B,A]`, identical to ours. Since that renderer was confirmed against
   Canary's output, Canary's ground truth is transitively in this chain.

Net: our channel order and the linear/UNORM interpretation are both **correct** for the ~85% RGBA
variants. A live Canary framebuffer capture would be redundant confirmation, not a new signal.

## Remaining / adjacent
- **XPR2** shares the ARGB→RGBA reorder + the linear/UNORM finding, but its **tiling** (de-tile) is a
  separate path; if XPR2 skyboxes still look odd it's tiling or viewer display-gamma, not channel order.
- **Viewer display gamma:** decode is correct; if egui shows these too dark/bright it's how egui
  interprets the RGBA8 (sRGB-encoded vs linear) at display time — a rendering nit, not a decode bug.

## Evidence log
- `2026-07-11` — static read of Canary `vulkan_texture_cache.cc` host-format table; no `R8G8B8A8_SRGB`
  entry exists → sRGB/UNORM claim `CONFIRMED`.
- `2026-07-11` — measured `==0xFF` alpha dominance over 789 disc T8aD textures (byte0 = alpha 97%) +
  cross-checked vs `xenia-rs decode_k8888_tiled` (the Canary-validated M1/M2 path). → channel-order
  claim promoted `HYPOTHESIS → CONFIRMED`.
