#1388: retain whole text raster for animator blur

This commit is contained in:
FredApps
2026-11-09 22:54:40 +01:00
parent 636dc1578c
commit 2d6d08f29f
3 changed files with 37 additions and 1 deletions
+1 -1
View File
@@ -488,7 +488,7 @@ impl<'a> Renderer<'a> {
|| styles::active(ctx, layer)
|| ctx.comp.layers.iter().any(|l| matches!(l.source, LayerSource::Light { .. }))
|| self.is_adv3d(ctx)
|| (matches!(layer.source, LayerSource::Text) && text::per_char_3d(ctx, layer))
|| (matches!(layer.source, LayerSource::Text) && (text::per_char_3d(ctx, layer) || text::animator_blur(ctx, layer)))
{
return None;
}
+26
View File
@@ -332,3 +332,29 @@ fn fine_clip_cells_have_distinct_cache_keys() {
assert_ne!(key(a), key(kurbo::Rect::ZERO));
assert_ne!(key(b), key(kurbo::Rect::ZERO));
}
/// Text animator Blur reads beyond the fixed anti-aliasing padding of a clipped window.
#[test]
fn animated_text_blur_keeps_offscreen_glyph_pixels_while_panning() {
let (mut p, cid) = scene(|p, c| text3(p, c, [W as f64 / 2.0, H as f64 / 2.0, -zoom() + 40.0], 0.0));
let mut next = p.next_id;
let mut ids = Ids(&mut next);
let props = build::text_anim_props(&mut ids, "blur", false);
let mut animator = build::text_animator(&mut ids, "Blur", props);
animator.prop_mut("properties/blur").unwrap().value = Value::Vec2([40.0, 40.0]);
p.next_id = next;
p.comp_mut(cid).unwrap().layers[0].props.group_mut("text/animators").unwrap().children.push(animator.into());
let opts = RenderOpts { scale: 0.125, ..Default::default() };
let cache = crate::LayerCache::default();
for dx in [-10.0, 0.0, 10.0] {
set(&mut p.comp_mut(cid).unwrap().layers[0], "transform/position", Value::Vec3([W as f64 / 2.0 + dx, H as f64 / 2.0, -zoom() + 40.0]));
let mut r = Renderer::new(&p, &NoFootage, opts);
r.cache = Some(&cache);
let ctx = r.eval_ctx(cid, Tick::ZERO).unwrap();
let layer = &p.comp(cid).unwrap().layers[0];
assert!(r.raster_clip(&ctx, layer).is_none(), "animator blur needs off-window pixels");
let mut whole = Renderer::new(&p, &NoFootage, opts);
whole.whole_raster = true;
assert_eq!(r.comp_frame(cid, Tick::ZERO).data, whole.comp_frame(cid, Tick::ZERO).data);
}
}
+10
View File
@@ -509,6 +509,16 @@ pub fn per_char_3d(ctx: &EvalCtx, layer: &Layer) -> bool {
layer.is_3d() && layer.props.sub("text").is_some_and(|t| ctx.b(layer, t, "perChar3d"))
}
/// Animator blur can read glyph pixels beyond a viewport raster window. Conservatively
/// keep the whole raster even when the current selector happens to select no characters.
pub(crate) fn animator_blur(ctx: &EvalCtx, layer: &Layer) -> bool {
layer.props.group("text/animators").is_some_and(|anims| {
anims.groups().filter(|anim| anim.enabled).any(|anim| {
anim.sub("properties").and_then(|props| props.get("blur")).is_some_and(|blur| ctx.value(layer, blur).as_vec2().iter().any(|value| *value != 0.0))
})
})
}
/// One drawn character.
#[derive(Clone, Debug)]
pub struct PlacedGlyph {