Repository navigation
Add batched per-glyph effects to BitmapText - #1725
Conversation
c78abf2 to
5a25623
Compare
Review follow-ups on top of @snowyukitty's work. `text.glyphEffect = undefined` threw `TypeError: effect is not a function` out of `draw()`, i.e. from inside the frame loop. The constructor already normalized with `settings.glyphEffect || null`, but the setter stored whatever it was given while the draw path tests `!== null`. `undefined` is both how an unset option arrives and how a caller spells "turn it off", and the getter also reported it, breaking its own documented `GlyphEffect|null` type. One test per backend covers it, and reverting the fix reproduces the TypeError. Dropped the `chars` cache. It was an array of characters per line, rebuilt in `setText` and again in the setter, to supply `context.char` — but the draw loop already holds the line, so `string.charAt(c)` is the same answer with no extra field, no second maintenance site and no invariant to keep in sync. `trimEnd()` only trims the tail, so the indices agree. Documented the Canvas cost honestly. "Prefer a finite colour palette" read as a performance hint; what actually happens is that `TextureCache#tint` keys a cached, tinted copy of the whole font page by colour, unbounded for the life of the renderer, so a colour driven by `ctx.time` allocates a page-sized canvas every frame on that backend. Also noted that the renderable reports itself dirty every frame while an effect is set. The UI and text skill now covers the hook. Animating letters was the one thing a reader would otherwise solve with the per-character renderable workaround this feature exists to remove, and the skill's own triggers already include "animate a label". Credited the contributor in the changelog entry, as the house convention asks for external contributions. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGvtaUNATVCVxD2qcbiY4t
5a25623 to
1cbcbdd
Compare
|
Thanks @snowyukitty — this is a well-built change. The approach is right: packing the tint into the per-vertex colour means an animated line stays one draw call, and the I rebased onto master (#1688 landed today and moved 1. A falsy assignment crashed the frame loop. 2. Dropped the 3. Said what Canvas actually costs. "Prefer a finite colour palette there" reads as a performance hint. What happens is that I also added a One thing left open rather than decided: #1522 also floated presets (wave / shake / rainbow) and 🤖 Generated with Claude Code |
Description
Adds the offset/tint primitive requested in #1522:
BitmapText.glyphEffect(out, ctx)modifies individual glyphs without creating a renderable for each character. The text example now uses one BitmapText for its wavy speaker name.The callback receives reused output/context objects, with offsets reset to zero and tint reset to opaque white before each glyph. Effects preserve advances, kerning, wrapping, alignment and typewriter reveal. Animation time advances in
update, and tint/opacity are restored even if the callback throws. The ordinary drawing path remains available when the callback isnull.This keeps effects independent of layout: measured bounds are unchanged, so offsets do not extend culling bounds. Canvas uses the existing tinted-image cache; the API documentation recommends a finite colour palette there. Presets, markup, scale and rotation are left for follow-up work.
Type of change
Checklist
pnpm lintpasses)pnpm testpasses)pnpm build)Validation
From the repository root, using Node 24, pnpm 10.32.1 and
CI=1:Core: 7,686 passed / 11 skipped; Planck: 221 passed; Matter: 226 passed; debug plugin: 23 passed. The unmodified core baseline was 7,673 passed / 11 skipped. The 12 new Canvas/WebGL cases cover reused state, scaled/wrapped layout, visibility, missing glyphs, update timing, exception restoration and actual pixels. The WebGL pixel case also verifies that different glyph colours still produce one draw call. A separate test checks the exported callback types.
Validated in Linux Chromium; dedicated WebGPU pixel validation and Windows execution were not run.
Related issues
Closes #1522.