fix(compositor): keep straight alpha in the paste-cache raster — the web widget's recording black box
The element-space raster builds on a TRANSPARENT base but BlitContentRaw's partial-alpha branch applied the opaque-dst source-over blend: color premultiplied by the sampled alpha, then alpha forced to 255. Pasting that raster saw a==255 and straight-copied darkened ink over the scene — a recording box that the raw-bitmap preview (correct alpha) never showed. Transparent margins and opaque content were unaffected, which is why every take looped on the page/CSS while the capture was transparent all along (15:51 dumps: alpha max 255, mean ~19, zero 57%). BlitContentRaw now takes transparentDst; the raster call passes true and writes straight color + straight alpha so the paste rows (BlendRowOpaque/Weighted) do the real source-over onto the opaque master. Master paths byte-identical. ONE integration test PasteCache_SemiTransparentLayer_RevealsBackdrop_NotOpaqueInk: 50%-blue over red reads (127,0,128) fixed vs (0,0,128) buggy — proven both ways (verified by stashing the fix: fails before, passes after). Clean build, 0 warnings; 22/23 compositor-class tests pass, the sole failure the documented pre-existing Composite_FullScene_MasterPixels pixel (1380,700). Alpha-compositing model: standard source-over with producer-cached surfaces, the OBS/libyuv paste model already cited in ai.md/MyMistakes (rawvideo recipe, row-blit BLEND_NONE / straight-alpha branches); full story in MyMistakes (RESOLVED entry). Per the good-dog rule: one integration test, memory updates (MyMistakes/ai.md/ HANDOFF) in the same commit.
This commit is contained in:
@@ -93,6 +93,29 @@ closes. Self-inflicted again: the agent re-derived the whole transparency story
|
||||
this entry — the instrument said transparent because the capture had NOT been
|
||||
repainted yet. Do NOT re-derive this story a third time.
|
||||
|
||||
**2026-09-12 → RESOLVED — the box lived in the paste-cache RASTER, not the page.**
|
||||
The 15:51 facts (alpha max=255, mean ~19, zero 57%, white rounded panel, real
|
||||
transparent margins) + preview-correct + recording-box meant the capture transparency was
|
||||
REAL all along; the fracture sat in `SceneCompositor.BlitContentRaw`
|
||||
(SceneCompositor.cs:419-550) — the sampler that builds the element-space paste-cache
|
||||
raster, the ONE path the take loop never read in full. Its partial-alpha branch applied
|
||||
the OPAQUE-dst blend onto a TRANSPARENT raster base: `dst = (src*sa + dst*inv)/255`
|
||||
with dst black → color PREMULTIPLIED by sa, then `dst[+3] = 255` — a 50%-alpha widget
|
||||
pixel became darkened color + FULL alpha. At paste time `BlendRowOpaque` saw alpha 255 →
|
||||
straight copy → the scene behind was overwritten by darkened ink. Fully-transparent
|
||||
margins (alpha 0, `continue`) and fully-opaque content (alpha 255 branch) survived —
|
||||
which is why every take showed a box while the dumps and the preview (raw WriteableBitmap,
|
||||
unaffected by the compositor) stayed correct, and why the CSS-wipe fixes (`b4bba4b` /
|
||||
`0f72c53`) were red herrings: they treated the page as the villain, but the capture was
|
||||
transparent from the start. **FIX:** `BlitContentRaw` gained `transparentDst=false`;
|
||||
the raster call site passes `true` and writes STRAIGHT color + straight alpha so the
|
||||
paste rows (`BlendRowOpaque`/`BlendRowWeighted`) do the real source-over onto the opaque
|
||||
master. The master paths are untouched (bit-identical). ONE test
|
||||
`PasteCache_SemiTransparentLayer_RevealsBackdrop_NotOpaqueInk` fails on the old code with
|
||||
exactly the bug encoded: 50%-blue over red reads `(0,0,128)` instead of `(127,0,128)` —
|
||||
backdrop never shows through. Next: verify take (element rect shows the backdrop behind
|
||||
the widget art), then push gate #1 clears.
|
||||
|
||||
### Shrink / re-encode an image for the README (screenshots → small hero image)
|
||||
|
||||
Worked out 2026-08-29 (the recipe was NEVER recorded the first time it was done, so
|
||||
|
||||
Reference in New Issue
Block a user