With Units set to Index the selector already counts characters (words,
lines), but the timeline kept showing Start, End and Offset as
percentages ("3.0%"), so switching looked like it did nothing. They are
now shown without the % sign while Units is Index
(render::text::in_index_units).
Synthetic bold grows a glyph's outline in its own space, so a
non-uniform character Scale stretches it more along one axis. The patch
and layer bounds reserved it through mean_scale and could clip the
stretched side; they now reserve how far the bold reaches along x and y.
A run of solid fills grows its glyphs together in layer space by one
radius, which only matches when the bold stays round there: a bold glyph
whose matrix stretches or skews is no longer gathered into a run.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The layout marks a glyph synth_bold when Faux Bold is on, or when a bold
style is asked of a family without a bold face, but the renderer never
read it, so both drew as regular.
PlacedGlyph now carries the bold radius (0.03 em, which brings Inter
Regular's stems to Inter Bold's). A bold fill is the outline's coverage
merged with a round stroke twice that wide, and a bold glyph's stroke is
the band between the outline grown by radius ± half the stroke width, so
it follows the bolder shape. Runs of solid fills only merge glyphs of the
same bold radius, and the patch and layer bounds grow by it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The Advanced 3D renderer folded Accepts Shadows (Off/On/Only) into a
bool, so a shadow catcher set to Only drew as an ordinary lit surface.
The material now carries shadows_only. Such a fragment draws black at
the strongest shadow a light casts on it, as Classic 3D does, and it
goes in the blended pass so what is behind it shows through. The CPU
shader and advanced3d.wgsl get the same branch.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
draw_bounds padded every stroke by width × miter limit / 2 on each side, joins or
not. A straight 1080 px wide wipe used as a track matte became a 4326×6366 layer
buffer (~440 MB as RGBA f32), made anew every frame while its trim animates, then
cached on the CPU and uploaded to the GPU. With 12 frames exported in parallel a
16 GB Mac ran out of memory and froze.
Now the miter limit applies only to miter-joined paths with a join (two or more
segments, or closed); square caps reach √2 half widths, other strokes one; Taper
and Wave keep the widest padding. Rendered pixels are unchanged.
A full-res frame of the affected ad: 1.7 GB → 0.7 GB peak.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A layer selected by another layer's effect (Displacement Map, Blend...) had
its structure, values, matrix and time hashed into the owner's key, but not
the comp camera/lights its own scene-reading effects (Caustics, CC Particle
World...) see. The owner's key included the scene only when the owner itself
ran such an effect, and a cached owner never runs the selected layer's
effects, so moving the camera or a light reused stale owner pixels.
Dependencies::layer now hashes comp_scene for every selected (or nested)
layer that reads it, with the same policy as owner keys.
Fixes#1252
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N9kwMcw2pYQ5fSjKa3ZPnh
Guide layers render only in their own comp's viewer: a nested renderer (precomp, collapsed
precomp in 2D, Classic 3D and Advanced 3D, comps read by effects, aux passes, the GPU walk's
resident precomps) now runs with guides off (Renderer::nested_opts), so a precomp's guide
layers no longer show in the parent comp's viewer or in a "Current Settings" render.
The Timeline layer and Composition viewer right-click menus (shared layer_menu) gain a
Guide Layer item (a plain toggle, no check mark) acting on the selection, and guide layers show a guide mark
before their name in the Timeline.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In After Effects solo is not time-dependent: while any AV layer is soloed,
non-soloed layers are excluded at every time. The renderer, nested/collapsed
3D paths, engine viewer helpers and viewer hit tests only counted solo layers
active at the current time, so outside the soloed layer's in-out range the
whole comp reappeared. Drop the time condition (audio already ignored it).
Update the #66 viewer regression test to the AE behaviour and add a render
test for it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Expansion was a fill plus a stroke 2·|expansion| wide, blended per pixel. Once the
expansion passed the shape's curvature radius the stroke's inner edge inverted: a
20 px circle expanded by 60 left a ring with the layer showing through the middle,
+600 cut only the original dot, and negative values left islands. The outline is
now offset with Offset Paths' geometry (round joins) and filled.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Merge Paths item appended to a shape layer's Contents cleared every draw
collected so far, including fills and strokes inside groups above it, so
the layer's shapes vanished from the render. AE's rule is per group: only
paint items in the merge's own group above it are dropped. Draws now
record which contents level created them; the merge drops only its own
group's paint, and the group-bubbled draws keep rendering.
Adds a regression test (Merge Shapes and Add modes) that renders nothing
without the fix.
Stretched layers copied the nearest source sample for each output sample, which adds error: at
48 kHz the worst error on a 440 Hz tone was 0.025, and 0.15 at 8 kHz. They now blend the two
source samples either side. Layers at 100% and 50% speed keep their exact samples.
The render tests cover a plain precomp, which mixes the same audio as its layers, and the stretch
error against the exact tone.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Echo, Posterize Time and the other Time effects read the layer at other times with none of
its effects. After Effects renders them with the effects before them: SceneYard "Task
Preparation UI" applies Echo after CC Particle Systems II on black solids, and AE shows the
echoed fireworks over a white page where we echoed the black solids and blacked it out
(Posterize Time after Turbulent Displace, the usual stop-motion setup, depends on the same).
The host already bounds the count to the effects before the one rendering.
Mean abs error vs AE: Task Preparation UI 0.1855 -> 0.0071, Lightbulb Idea 0.0126 -> 0.0102;
the 15 other corpus projects with a Time effect after another effect are pixel-identical.
(cherry picked from commit 46870452db2656c72406f63245e8b1609ceb04d4)
(cherry picked from commit 334d102582e5a64db32e2f3e55836d55ac96bdaa)
(cherry picked from commit 734da3e35dc57c01b73823458bdd87611ac1bf99)
(cherry picked from commit 87a733e076)
- Renderer: no cover step for adjustment layers (#686); their buffer is the comp below.
Restores a single CPU step in split adjustment stacks.
- Effect Controls rename (#953): focus the name field the frame it exists. Focusing an id
not yet drawn left AccessKit with a focus outside its tree (a panic in the consumer).
The tests double-click at 60 fps, within egui's double-click time.
- i18n: Russian, French and German entries for Copy Frame to Clipboard and the extrusion
Front/Bevel/Side/Back Color submenus added by today's other PRs.
- Tests: "fr" is now a valid language; the marker dialog's comment is focused on open
(#922), so Enter-is-OK is tested with the focus elsewhere; the toolbar gradient tests
give the selected layer its paint kind, since the toolbar mirrors it (#732).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Footage sources: the cancellable wrapper (#784) forwards frame_with (#752 float curves)
and problem (#887); problem's default lives on the trait.
- Media: a called-off decode (#784) is not a footage problem (#887).
- Effect Controls rename (#953): Escape cancels (egui drops focus on Escape the same
frame), the Rename item is translated; regression test.
- i18n (#873): drop the /out-i18n/ ignore rule and the note about an uncommitted script.
- Tests: text range selector caps (#926) re-borrow the layer; the RAM preview test (#793)
sets the comp's own Resolution (#462).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Shape layers are always continuously rasterised, as in After Effects and
as text layers already are here: the contents are drawn at the layer's
effective scale (own transform and parents) whether or not the
Continuously Rasterize switch is on, so scaled-up shapes stay sharp.
Fixes#751
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>