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:
@@ -758,7 +758,13 @@ seam:** `Func<Scene?>`, `Func<SceneElement, VideoFrame?>` resolver, `Func<Compos
|
||||
routes non-opaque layers through `BlitCachedLayer`: a layer rasterizes ONCE into an element-space,
|
||||
TRANSPARENT-based frame keyed by (source-array identity, source W×H, ceil'd dst rect, round,
|
||||
mirror), then every later tick PASTES it (integer position, row alpha-blend, opacity applied at
|
||||
paste). Producers hand out fresh immutable arrays, so array-identity keys can never serve stale
|
||||
paste). **Correction (2026-09-12, the web-widget "black box"):** the raster builds on a
|
||||
TRANSPARENT base, but the sampler's PARTIAL-alpha branch used the opaque-dst blend — it
|
||||
premultiplied the color into RGB and forced `alpha=255`. A translucent widget pixel then pasted
|
||||
as opaque darkened ink (the box in recordings) while the raw-bitmap preview stayed correct.
|
||||
`BlitContentRaw` now takes `transparentDst` (the raster call passes `true` and writes straight
|
||||
color + straight alpha; the paste rows do the source-over). Master paths are bit-identical.
|
||||
Guard: `PasteCache_SemiTransparentLayer_RevealsBackdrop_NotOpaqueInk`. Producers hand out fresh immutable arrays, so array-identity keys can never serve stale
|
||||
content; dict bounded at 48, cleared wholesale on overflow. Only changing content (webcam device
|
||||
frames, web capture ticks, chat messages) resamples; the opaque backdrop keeps its memcpy path.
|
||||
Position/opacity drags are now near-free (no resample — paste params, not cache keys).
|
||||
|
||||
Reference in New Issue
Block a user