* fix(svg): support static SVG patterns
* fix(svg): cache patterns per viewport
* fix(svg): share cached pattern content
* fix(svg): harden pattern cache keys
* fix(svg): harden pattern inheritance and caching
* Preserve SVG pattern fallback paint
* Format SVG pattern fallback conversion
* Harden SVG pattern resource bounds
* Fix SVG pattern test formatting
* fix(svg): bound hostile path and raster work
* Bound SVG ellipse expansion
* fix(ui-egui): suppress clippy warnings on wasm target
Rename unused `frame` parameter to `_frame` in draw_inner since
pointer_wrap::finish_native is cfg'd out on wasm32. Guard State::disabled
field with #[cfg(any(not(target_arch = "wasm32"), test))] so it only
exists where it's read (finish_frame on native + tests).
* fix: resolve clippy warnings blocking CI
- Rename unused `frame` parameter to `_frame` in draw_inner (wasm target)
- Guard State::disabled field with cfg(native + test) (wasm target)
- Replace chunks_exact(2) with as_chunks::<2>() in SVG polyline/polygon parsing
* fix(svg): budget raster work by affected region and report refusals
Address review on #2732: the finite-work guards charged the whole canvas,
dropping valid sparse art (8x8 image scaled to 1px on 2049x2049, 2x2 rect
under a 1,028-element clip on 512x512, one of six 2x2 rects sharing a 1x1
pattern on 3840x2160).
- Build fill/stroke/image/clip coverage over the device region only and
charge that region; remaining whole-canvas passes are charged to a
render-wide pixel budget (caps are not raised).
- Add try_rasterize/try_rasterize_with returning RasterError; rasterize
keeps its signature. media layered decode now surfaces refusals.
- Pattern content reached through href inherits styles from the owning
pattern; device_region falls back to the full canvas on non-finite input.
- Document new items and the limits in the svg README; regression tests.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Decide visibility from the panels drawn last frame instead of re-deriving the
maximize rule: a maximized frame shows its front tab, which may be Effect
Controls, and hides floating windows. Leave arrows to viewer text editing.
Replace the floating-beside-maximized case (a maximized frame hides floating
windows too) with hidden and on-screen cases, wait for the rename field the
way the other rename tests do, and keep viewer text editing's arrows.
Reload Footage Changed on Disk of a file whose metadata didn't change keeps the
project's revision; PurgeCaches emptied the viewer frame cache but the new
frame came back under the key of the one on screen, so neither the Composition
viewer nor the passive viewers uploaded it. Count purges and mix the count into
the frame key's options, as #3493 does with the LUT generation.
A docked panel's solid bars, once shown, took their width from the viewport, so a window that exactly filled its dock stayed too large and kept its bars. The bars are now hidden while the window fits its dock (to the whole point, #2585) and stay solid when it overflows (#269).
The viewers replace their picture only when the frame key changes, and a
frame rendered again after a LUT change had the same key, so the old look
stayed on screen. The viewer frame keys carry the LUT generation (in memory
only), which also leaves frames still rendering with the old LUT unasked
for, so the refusal of late frames is no longer needed.
The watcher stamped only the files whose parsed LUT was still cached, so
with more LUTs than the cache keeps, an edit to an evicted one left the
viewer's frames stale. The cache now remembers the stamp each LUT file was
last read with (bounded separately), and keeps at most 64 LUTs and 512 MiB
of tables. Frames finishing while the viewer empties its cache are refused.
Renders now follow a LUT file, but the viewer and RAM preview kept the
frames already rendered with the old one. A small worker stamps the LUT
files in use every two seconds and wakes the interface when one changed;
the interface then drops its frames, and a frame whose render started
before the change is not kept when it finishes.
* fix(flatpak): allow release checks and self-update helpers
* test: keep Flatpak permission parsing within strict Clippy rules
* chore: bump version to 0.24.1 for the Flatpak update fix
* UI: ScriptUI images decoded within a size limit; effect detection answers each comp once
ScriptUI decoded any image and converted it to full RGBA without a size
check; images are decoded through image's limits (4096 px per side and a
matching allocation cap). comp_has_effects walked a nested comp again for
every layer using it; each comp is now answered once.
* address review: effect detection by breadth-first search so depth cannot hide a comp
The prompt showed 3 detail lines per category under a bare count, which read
as the project's total. The headline now says how many occurrences are left
out or changed, every line the report keeps shows, and "… and N more" counts
the rest. The import report records that number as `unlisted` (an unmapped
effect's line covers all its instances); unmapped effects are listed most
used first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
picks_what_the_viewer_shows_across_sizes_scales_zoom_pan_resolution_and_proxies looped over four
independent window-size and display-scale cases, each with its own session. As one test it took
~90 s in the gate and ~590 s under coverage, where it alone set the slowest SonarCloud shard.
Each case is now its own test, running the same checks, so nextest runs them in parallel and
partitions them across shards. The "more than 100 hovers checked" floor becomes more than 25 per
case, which still adds up to more than 100.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In a comp whose Start Timecode is negative, the current-time display shows
the first frames as `-0:00:00:12`, but typing that back (the Go to Time
dialog opens with it since #3419, and the Timeline's time field takes it)
read the leading `-` as "move back", so OK on an unedited dialog jumped to
another frame. There, a `-` before a full timecode is now the time's sign,
as it already is before Feet + Frames; a bare `-n` still steps back, and
comps that start at 0 or later are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With Fit New Layers to Work Area on and the playhead outside the work
area, the Type tool, shape tools and Pen now move the playhead to the
work area start so the new layer and its controls show. Layer timing is
unchanged; Layer ▸ New commands leave the playhead.
The pace counted cached frames among the next ahead x 2, but the prefetch never requests more than the RAM preview holds (offsets 0..fit-1 from the current frame). With room for only a few frames the lead could never fill, so a preview that rendered every frame at once was slowed to a fraction of its frame rate, with Lower Resolution When Playback Can't Keep Up off (the default). The lead is now counted over at most the frames the prefetch keeps after the current one, and with none of those (room for at most two frames) the clock runs in real time as before pacing.
The dialog's Frame field held the engine's zero-based frame, so with frames shown
from 1 the viewer's 00013 opened as 12, and typing 13 went to the frame displayed
00014. It now holds the time in the project's display style (timecode, frames from
Start Numbering Frames At, or feet + frames), which time.set already reads that
way. The now-unused 'Frame' translation rows go.
The step-down was skipped while the achieved frame rate read as keeping up, and that rate comes from the frames shown in the last second they were shown: through a stall it kept the rate from before it, so a preview that played a cached stretch and then stalled never stepped down. A rate whose last frame is more than a second old no longer counts.
The time display in the Composition panel's bottom bar only reacted to
Ctrl/Cmd+click, which switches timecode and frames. A plain click now opens the
Go to Time dialog (View > Go to Time), as in After Effects. The Ctrl/Cmd+click
test follows: its plain click now opens the dialog, which it closes.
It never steps back up during a play, so a looping preview kept playing at a quarter of its resolution after its first loop. Without it the paced first loop caches the viewer's resolution and the next loops play in real time, as in After Effects. The setting stays for whoever prefers a lower first pass.
Only a lane's drain thread waits for a GPU slot. A lane runs one drain at a time (it is idle, and started again, only once its drain found no job), and its other threads only help with that frame's parallel work, as Rayon helpers that never wait. The comments said lanes had one thread; the test now runs on a lane LANE_WIDTH wide and checks that its other threads decline while the drain thread waits.
AutoPick::ticket chooses a side and reports the frame's time once; dropped without one it ends the frame unmeasured. The viewer's frames, the renderer's top-level frames and frames rendered in passes use it, so an Auto CPU frame turned away by GPU admission (#3028), a cancelled export frame or any later exit no longer leaves its frame counted as running. The frame server keeps its choice across calls and still ends a replaced frame itself.
The paced clock counted the cached lead out of twice the prefetch depth even when fewer frames were left before the end, so a fully cached preview that doesn't loop slowed down over its last frames, and one shorter than that depth played slow throughout (and could step down). The lead now counts out of the frames left.
A GPU frame that failed or failed its health check, and a remote frame replaced before it finished, stayed counted as running on its side, so every later frame there was timed as one of several side by side and that side looked cheaper. AutoPick::abandoned releases them.