Upstream split lib.rs files into modules and moved engine commands/ to cmd/; this branch's change merges without textual conflicts.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Upstream split lib.rs files into modules and moved engine commands/ to cmd/; this branch's change merges without textual conflicts.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Opening the editor keeps the keyframe and mask-vertex selection as well as the property one,
and Duplicate cleared only the properties. With the copy selected, Clear then deleted a key or
vertex of the original instead of the copy. During an edit the copies now take the selection the
way a click on them does: layer.select's rule, moved into one helper both use. With no edit
open, Duplicate's selection is unchanged.
H.264 and ProRes exports of a 60-frame comp (several render batches, a
rotating stroked ring, a fading solid at a fractional position, a
Screen-mode shape, 8 bpc) must keep the exact bytes main wrote before the
two previous commits.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Much of a layer buffer is often transparent (a stroked ring, a glow-padded
text layer, a precomp canvas), yet every destination pixel sampled it
bilinearly, and 8/16 bpc comps then re-quantised the layer's whole rotated
bounding box.
- Bilinear warps skip 16x16 source tiles whose pixels (and a one-pixel
apron) all have zero alpha: their samples are transparent and were never
drawn.
- The fast affine warp reports the rectangle it changed and, given the
comp's levels, quantises each pixel as it blends it, so draw_comp has
nothing left to re-quantise for that layer. quantize_px moves to
effectcraft-raster for this.
- quantize_rect skips runs of four all-zero (+0.0) pixels, which quantise
to themselves.
- to_rgba8 / to_rgba8_over convert row by row instead of per 4-byte item.
Every pixel stays bit-identical: tests compare the warp against the
reference path, fused quantising against quantising after blending, and
the zero-run skip against per-pixel quantising.
With the previous commit, demo comp to H.264 (`cargo xtask bench --scenario
demo --format h264 --frames 300`, 11 interleaved runs on a 16-thread Ryzen):
wall 9.26 s -> 6.97 s median (-24.8%), 8.55 s -> 6.55 s min (-23.3%); CPU
time -23%. Output byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Movie exports (H.264, HEVC, AV1, ProRes, uncompressed) rendered a batch of
frames on every core, then encoded it frame by frame while the render
threads waited. Each batch now encodes on a pool shared by all exports while the
next batch renders (Cx::frames_beside). With deferred GPU readbacks, or
where no thread can be started (wasm without threads), the two still take
turns. A panicking encoder becomes an export error.
H.264 frames are converted to 4:2:0 on the render threads, per frame, instead
of on the encoder's critical path. The conversion reads 8-bit levels from a
table and rounds without a libm call; both give the same values as before
(tested against the previous formula).
Demo comp (1920x1080, 300 frames) to H.264 on a 16-thread Ryzen, export
wall time -6% (median of 7 interleaved runs); output byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The engine's text-edit tests repeated one layer-count closure in five places; they now use
the layer_count helper the 3D tests already had. The doc of forwardable_during_text_edit and
of the Type-tool test helper still spoke of Deselect All alone, though the editor now forwards
Duplicate too.
Duplicate passes over the property selection an open edit kept and copies the edited layer,
but left that selection in place on the original. With the copy selected, the next Duplicate
copied the original's effect, shape item or text animator, and Clear would have removed it.
Ending the edit through Duplicate now clears that property selection along with it.
Opening the editor keeps the property selection, so Cmd+D while typing duplicated an
effect, shape item or text animator selected before and left the edit open. An open edit
now comes first, as it does for Clear: Duplicate ends it and copies the edited layer. With
no edit open, selected effects and items keep their precedence, and the command's
description says so.
Main moved the engine's topic tests to src/tests/ and commands/ to cmd/. The
engine tests go to tests/text_edit.rs, opening their edit with its editing()
helper; ui_text_edit.rs keeps both sides' tests, this branch's first.
Conflicts came from upstream splitting crates/project/src/lib.rs and moving the
frames tests. Re-applied the branch's change in the new places:
- Layer::same_pixels_with now in crates/project/src/layer.rs
- Comp::same_pixels_with, same_settings(o, with_markers), pixel_form_with and the
timing-marker helpers in crates/project/src/comp.rs (same_pixels_ignoring_markers
stays removed)
- Project::reads_markers in crates/project/src/item.rs
- crates/ui-egui/src/frames.rs uses reads_markers/same_pixels_with; the branch's
two marker tests replace the old ones in crates/ui-egui/src/frames/tests.rs
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
text/layout: upstream split paragraph() into Para/place_line helpers and moved the tests to
layout/tests.rs. The branch's changes are re-applied there: the resolved paragraph direction
is passed to margins_for/avail_for/align_for (RTL point text with Justify sits flush right),
first_ascent/plausible_first and ParaLine.first/style_first (box first baseline is the d
height, vertical text keeps the ascent), and the Justify All last-line cost in
optimal_breaks. The branch's seven new layout tests moved to layout/tests.rs.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Conflicts: crates/ui-egui/src/menus.rs (upstream split it into menus/{bar,commands,entries,frontend,invoke,shortcuts,tests}.rs) and panels/settings.rs (ComboBox is now craft_ui::ComboBox).
- menus.rs: took upstream's slim module; re-applied the branch's two hunks in the new locations: nudge() now also skips Effects & Presets and Render Queue (menus/invoke.rs), and Enter is left to those panels in handle_shortcuts (menus/shortcuts.rs).
- panels/settings.rs: kept the branch's accessibility lines (shown_name, value_text) on top of craft_ui::ComboBox.
The accessibility audit BASELINE still matches the measured findings.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
render: upstream moved the tests_*.rs modules into crates/render/src/tests/; the branch's
tests_mask_feather.rs now lives at tests/mask_feather.rs and is registered in tests.rs.
path: rect() keeps the branch's explicit-outline version with an added NaN guard on the
roundness clamp bound (main's fix for the same panic).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* fix(ui-egui): craft_ui::ComboBox for every combo box, so open menus grow back (egui#5225)
Repins craft-libs to aiongg/craft-libs 60d60d48 (storytold main with the shared
dropdown) and bans egui::ComboBox in clippy.toml.
* fix(ui-egui): font menu under the font field is a craft_ui combo menu that grows back
The filter row is the menu's header and only the font list scrolls, so a search
that matched nothing grows the menu back when it is cleared.
* chore(deps): craft-libs 4d7b82f7 (dropdown fixes)
* chore(deps): craft-libs 92d392cf (#387 merged)
* chore(xtask): pdf-deps expects craft-libs 92d392cf
dominant_direction weighted texels by their raw channel sum before the
light's colour tinted the radiance, so equally bright red and blue regions
on opposite sides cancelled to no sun and no key — while a red Environment
light's tint removed the blue contribution and left the scene lit
directionally red, with no shadow for its Cast Shadows to show. The tint
now weights the direction sum (a white light weights [1; 3], a coloured
one its normalised colour), so the sun and key are derived from the
radiance the light actually emits; add_environment computes the tint once,
before the direction pass, and reuses it for the texel tinting. An
achromatic image is unaffected (its weights scale uniformly), so the
default studio and white-lit scenes keep their shadows exactly.
Tests: opposed red and blue texels cancel to a white light but a red tint
points the sun straight at the red one (a blue tint, at the blue one); end
to end, a red Environment light over an image with equally bright red and
blue regions on exactly antipodal positions casts the shadow a white light
over the same image does not — and the shadow falls opposite the red
region, the side the tinted radiance comes from.
Two review points on the merge:
- env_pixels returned None both for an unconfigured light (no Source, no
Environment Layer) and for a failed source (a deleted layer, missing
footage, a failed decode), so a selected source that failed silently
swapped in the default studio environment — relighting and reshadowing
the scene behind the user's back. It now tells the three apart
(EnvPixels::Absent/Image/Failed): only the unconfigured light uses the
default; a failed source contributes nothing, exactly like a layer whose
source fails renders nothing. The repair the user can act on stays where
it already lives — missing footage is flagged on the item, with the
relink commands to fix it.
- The docs claimed only a uniform image casts no shadow. A non-uniform one
can be directionless too: opposing bright regions cancel, and anything
under 1% directional is rejected. agents.md, architecture.md and the
sun/dominant_direction doc comments now say an image without a
sufficiently strong dominant direction.
Tests: an unreadable Environment Layer (auto-picked), an unreadable
selected Source and a deleted one all light nothing — pixel-equal to the
unlit render, no studio default, no default-environment shadow — while an
unconfigured light still lights; two equal bright spots at opposite
azimuths cancel to no sun and no key, and tilting the balance re-emerges
pointing at the brighter spot.
supported() mapped only ja, uk, es, cs, ka, pt and zh, so Match System
on a ru, de or fr system (ru-SK, de-DE, fr-CA) fell back to English
although all three have menu catalogs. And entry() returned the shown
label before the catalog lookup for every language without its own
undo/redo wording, so choosing Deutsch, Русский or Français by hand
still left the menus in English (#1449).
supported() now maps ru, de and fr. entry() keeps the undo/redo wording
as an Option: a language without it keeps the English undo/redo label,
and its other entries go through the catalog. Russian gets its undo and
redo wording.
A soloed model keeps its Solo switch with its video off, and any_solo
hides non-soloed layers: the copy was excluded and the comp rendered
blank. The copy now takes the Solo over; the source's video stays off.
Covered by instance3d_keeps_the_copy_visible_when_the_source_is_soloed.
PR #2491 moved the modules; the instance3d changes follow the new layout:
- tests_instance3d.rs lives in src/tests/instance3d.rs now (declared in src/tests.rs)
- solo_3d moved from render/src/lib.rs to render/src/pipeline.rs, pub(crate),
and its FxHost::layer caller moved to render/src/fx_host.rs
- cmd/create.rs, effects/channel2.rs, gpu/fx_pixel2.rs and render/cache/dependencies.rs
followed their moves automatically (rename detection)
Upstream moved crates/ui-egui/src/menus.rs into menus/*.rs; the dynamic-menu change that
draws script folders as submenus now lives in menus/bar.rs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The default studio environment's level-preserving mean of 1 acted like a
100% white Ambient light: with only the Environment light on, a white floor
rendered at full white (95% of the frame saturated) while its shadow core
went to pitch black — a fog over the scene, nothing like After Effects'
default. Three changes bring the look in line, CPU and GPU alike:
- The default environment is a dim studio now: a solid-angle-weighted mean
of 0.3 (and a stronger key gradient), so a white floor reads a mid gray
and the light's colour x intensity still scales the level.
- The shadow occludes only the image's directional (key) fraction of the
light — |sum of dir x radiance| / sum of radiance, computed beside the
dominant direction and carried in the scene — so the fill keeps shining
inside the shadow: an area-light shadow, partial like After Effects'
(a lone bright spot on black still shadows almost fully; a uniform image
not at all).
- The environment shadow map's penumbra has a floor — eight output pixels,
or four map texels where those are coarser — because the map covers the
whole scene bounding sphere rather than the view, and its texels would
otherwise stair-step the edge (all the more in a draft render's flat 512
budget). Shadow Diffusion widens it further, as before.
Tests: the key fraction tracks directionality; the default keeps its dim
level, its direction and its key; the default and a bright key both shadow
the floor (the default partial, the key deep) with Cast Shadows off lit
everywhere; and the full look — a mid-gray lit floor (no fog), a partial
core, and a soft edge ramp in quality and draft renders alike.