An Open link action carries a URL from the document and fires from whatever
trigger the prototype sets, so an unattended presentation could open web links
and mail messages one after another: a mail link bypassed the leaving-the-
prototype notice entirely, and a skipped notice let any trigger open any
number of links.
- finish() routes every openable URL, mail included, through the notice.
- Skipping the notice ("Don't show this again") opens at once only a link the
viewer's own click, press, drag or key fired, one per frame. A delay, the
pointer passing over a layer (enter, leave, hover) and a timed mouse
trigger that fires after the gesture is over still show the notice, which
then offers no checkbox: it couldn't skip that notice anyway.
- The player tags each queued link with whether the viewer fired it
(Presenter::fire_from); an OpenUrl outside an interaction defaults to not.
- The notice covers mail links (new locale string, mail wording).
- Engine prefs/command doc text follows the rule; behavior untouched there.
- present_links_tests: shared setup helpers for the link tests; new
tests for the skip rule (delay, hover, delayed mouse-up, key, one link per
click, checkbox only for clicked links); the scheme-filter test now clicks
with the notice skipped so the filter alone keeps those URLs out.
- docs/prototyping-parity.md row, status line and revision history updated.
Shadows and layer blurs dominated PNG/JPG/WebP export time (gdb stack
sampling of the example files: about 45% in fx::box_blur_h, 13% in
fx::morph). Both now produce the same bytes, faster:
- fx::blur runs its three box passes per row while the row is in cache,
works on whole pixels instead of bounds-checked bytes, skips fully
transparent rows, transposes in cache-friendly column blocks, and
splits rows and transposes across rayon on desktop for pixmaps of
64K pixels and more, only when not already on a rayon worker (a band
of render_view stays on its thread: no nested parallelism).
- fx::morph (shadow spread on layers that aren't one outline) uses the
van Herk / Gil-Werman running max/min: constant work per pixel instead
of 2r + 1.
Integer arithmetic and pass order are unchanged; new tests compare both
against the previous implementations byte for byte, including sizes on
the parallel path.
An ignored timing test exports every top-level frame of examples/ as
PNG, JPG and WebP at 2x through export_file:
cargo test --release -p figcraft-engine --lib export_timings -- --ignored --nocapture
Ryzen, 16 threads, Linux; median of 7 (min):
PNG 2195 ms (2182) -> 992 ms (989) -55%
JPG 2880 ms (2560) -> 1550 ms (1535) -46%
WebP 1964 ms (1901) -> 1022 ms (1018) -48%
total 7035 ms (6677) -> 3567 ms (3555) -49%
All 80 exported files (PNG, JPG, WebP, PDF, SVG) are byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>