# Conflicts:
#	crates/lottie/src/tests.rs
This commit is contained in:
Brandon Thomas committed 2026-11-03 09:05:06 -05:00
commit 072db0ca35
3 files changed
+33 -4

No files matched your search

+2 -1
View File
@@ -316,7 +316,8 @@ fn masks(im: &Im, ids: &mut Ids, list: &[Json], g: &mut PropGroup) {
import_prop(im, j, p, conv);
}
};
set("pt", "path", &shapes::to_path);
let closed = m.get("cl").and_then(Json::as_bool).unwrap_or(false);
set("pt", "path", &|v| shapes::to_path(v, closed));
set("o", "opacity", &anim::to_scalar);
set("x", "expansion", &anim::to_scalar);
set("f", "feather", &anim::to_vec2);
+6 -3
View File
@@ -27,7 +27,9 @@ pub(crate) fn path_value(v: &Value) -> Json {
}
}
pub(crate) fn to_path(j: &Json) -> Option<Value> {
/// A Lottie path. `closed` is used when the path has no `c`: Bodymovin 3.x and early 4.x keep the
/// flag outside the path, as `cl` on a mask and `closed` on an `sh` item.
pub(crate) fn to_path(j: &Json, closed: bool) -> Option<Value> {
let o = match j {
Json::Array(a) => a.first()?,
o => o,
@@ -48,7 +50,7 @@ pub(crate) fn to_path(j: &Json) -> Option<Value> {
in_tangents: fit(pts("i")),
out_tangents: fit(pts("o")),
vertices,
closed: o.get("c").and_then(Json::as_bool).unwrap_or(false),
closed: o.get("c").and_then(Json::as_bool).unwrap_or(closed),
feather: Vec::new(),
}))
}
@@ -493,7 +495,8 @@ pub(crate) fn import_items(im: &mut Im, ids: &mut Ids, items: &[Json]) -> Vec<Pr
"sh" => {
let mut g = build::shape_path(ids, ShapePath::default());
dir(&mut g);
set(im, &mut g, "path", j.get("ks"), &to_path);
let closed = j.get("closed").and_then(Json::as_bool).unwrap_or(false);
set(im, &mut g, "path", j.get("ks"), &|v| to_path(v, closed));
g
}
"fl" => {
+25
View File
@@ -533,6 +533,31 @@ fn embedded_glyphs_are_reported_when_dropped() {
assert_eq!(warnings(&blank), ["embedded glyphs (`chars`) are not imported; text uses the installed fonts"]);
}
#[test]
fn legacy_closed_flags_close_masks_and_paths() {
// Bodymovin 3.x and early 4.x put the closed flag on the mask (`cl`) and on the `sh` item
// (`closed`), with no `c` in the path. An open mask would leave the solid whole.
let square = json!({"i": [[0, 0], [0, 0], [0, 0], [0, 0]], "o": [[0, 0], [0, 0], [0, 0], [0, 0]], "v": [[5, 5], [15, 5], [15, 15], [5, 15]]});
let doc = json!({"w": 20, "h": 20, "fr": 30, "ip": 0, "op": 30, "assets": [], "layers": [
{"ty": 4, "ind": 1, "ip": 0, "op": 30, "st": 0, "ks": {},
"shapes": [{"ty": "sh", "closed": true, "ks": {"k": [{"t": 0, "s": [square], "e": [square]}, {"t": 10}]}}]},
{"ty": 1, "ind": 2, "sc": "#ff0000", "sw": 20, "sh": 20, "ip": 0, "op": 30, "st": 0, "ks": {},
"masksProperties": [{"mode": "a", "cl": true, "pt": {"k": square}}]}]});
let mut q = Project::default();
let nc = import(&mut q, doc.to_string().as_bytes(), "Imported", "", &mut |_| None).unwrap().comp;
let comp = q.comp(nc).unwrap();
let closed = |v: &Value| matches!(v, Value::Path(sp) if sp.closed);
let shape = comp.layers[0].props.sub("contents").unwrap().groups().next().unwrap();
let keys = &shape.get("path").unwrap().keys;
assert_eq!(keys.len(), 2);
assert!(keys.iter().all(|k| closed(&k.value)));
let mask = comp.layers[1].masks().unwrap().groups().next().unwrap();
assert!(closed(&mask.get("path").unwrap().value));
let img = render_frame(&q, nc, s(0.0), 1.0);
assert_eq!(img.data[0][3], 0.0, "the mask cuts the corner away");
assert!(img.data[10 * 20 + 10][3] > 0.99);
}
#[test]
fn precomp_with_time_remap() {
let (mut p, cid) = setup();