Merge pull request #25 from storytold/pr/never-crash-lottie
Never crash: Lottie enum values can't overflow
This commit is contained in:
commit
d350f4e7ac
|
|
@ -372,5 +372,5 @@ pub(crate) fn to_bool(j: &Json) -> Option<Value> {
|
|||
|
||||
/// Lottie 1-based enum → 0-based popup index.
|
||||
pub(crate) fn to_enum1(j: &Json) -> Option<Value> {
|
||||
to_scalar(j).map(|v| Value::Enum((v.as_f64().round() as i64 - 1).max(0) as u32))
|
||||
to_scalar(j).map(|v| Value::Enum((v.as_f64().round() as i64).saturating_sub(1).max(0) as u32))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
//! Keyframe times in Lottie are frames of the layer's local time, which is EffectCraft's layer
|
||||
//! time; layer `ip`/`op`/`st` are composition frames and `sr` is the stretch factor.
|
||||
|
||||
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
|
||||
|
||||
mod anim;
|
||||
mod base64;
|
||||
mod export;
|
||||
|
|
|
|||
|
|
@ -359,10 +359,10 @@ fn import_blend(j: &Json, g: &mut PropGroup) {
|
|||
fn import_stroke(im: &Im, j: &Json, g: &mut PropGroup) {
|
||||
set(im, g, "width", j.get("w"), &anim::to_scalar);
|
||||
if let Some(lc) = int(j, "lc") {
|
||||
set_enum(g, "cap", (lc - 1).clamp(0, 2) as u32);
|
||||
set_enum(g, "cap", lc.saturating_sub(1).clamp(0, 2) as u32);
|
||||
}
|
||||
if let Some(lj) = int(j, "lj") {
|
||||
set_enum(g, "join", (lj - 1).clamp(0, 2) as u32);
|
||||
set_enum(g, "join", lj.saturating_sub(1).clamp(0, 2) as u32);
|
||||
}
|
||||
if let Some(p) = g.get_mut("miter") {
|
||||
if let Some(ml) = j.get("ml").and_then(Json::as_f64) {
|
||||
|
|
@ -541,7 +541,7 @@ pub(crate) fn import_items(im: &mut Im, ids: &mut Ids, items: &[Json]) -> Vec<Pr
|
|||
"op" => {
|
||||
set(im, &mut g, "amount", j.get("a"), &anim::to_scalar);
|
||||
if let Some(lj) = int(j, "lj") {
|
||||
set_enum(&mut g, "join", (lj - 1).clamp(0, 2) as u32);
|
||||
set_enum(&mut g, "join", lj.saturating_sub(1).clamp(0, 2) as u32);
|
||||
}
|
||||
if let (Some(ml), Some(p)) = (j.get("ml").and_then(Json::as_f64), g.get_mut("miter")) {
|
||||
p.value = Value::Scalar(ml);
|
||||
|
|
@ -561,7 +561,7 @@ pub(crate) fn import_items(im: &mut Im, ids: &mut Ids, items: &[Json]) -> Vec<Pr
|
|||
}
|
||||
_ => {
|
||||
if let Some(m) = int(j, "mm") {
|
||||
set_enum(&mut g, "mode", (m - 1).clamp(0, 4) as u32);
|
||||
set_enum(&mut g, "mode", m.saturating_sub(1).clamp(0, 4) as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -725,3 +725,31 @@ fn text_style_runs_export_as_range_animators() {
|
|||
// Played back as an animator, it looks the same.
|
||||
compare_renders(&p, cid, &q, nc, 2.0 / 255.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extreme_enum_numbers_do_not_overflow() {
|
||||
// Line cap / join / merge mode / text-selector enums are 1-based; a huge negative number
|
||||
// (-1e308 rounds to i64::MIN) overflowed the `- 1`.
|
||||
let path = format!("{}/tests/fixtures/shapes-gradient.json", env!("CARGO_MANIFEST_DIR"));
|
||||
let mut doc: Json = serde_json::from_slice(&std::fs::read(path).unwrap()).unwrap();
|
||||
let mut hits = 0;
|
||||
fn patch(j: &mut Json, hits: &mut usize) {
|
||||
match j {
|
||||
Json::Object(o) => {
|
||||
for k in ["lc", "lj", "mm"] {
|
||||
if o.contains_key(k) {
|
||||
o.insert(k.into(), json!(-1e308));
|
||||
*hits += 1;
|
||||
}
|
||||
}
|
||||
o.values_mut().for_each(|v| patch(v, hits));
|
||||
}
|
||||
Json::Array(a) => a.iter_mut().for_each(|v| patch(v, hits)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
patch(&mut doc, &mut hits);
|
||||
assert!(hits > 0);
|
||||
let mut q = Project::default();
|
||||
import(&mut q, doc.to_string().as_bytes(), "x", "", &mut |_| None).unwrap();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -363,7 +363,7 @@ pub(crate) fn import_text(im: &mut Im, ids: &mut Ids, t: &Json, tg: &mut PropGro
|
|||
let mut en = |m: &str, key: &str, max: u32| {
|
||||
let v = s.get(key).and_then(|v| if v.is_object() { v.get("k") } else { Some(v) }).and_then(anim::nums).and_then(|n| n.first().copied());
|
||||
if let (Some(v), Some(p)) = (v, adv.get_mut(m)) {
|
||||
p.value = Value::Enum(((v.round() as i64 - 1).max(0) as u32).min(max));
|
||||
p.value = Value::Enum((((v.round() as i64).saturating_sub(1)).max(0) as u32).min(max));
|
||||
}
|
||||
};
|
||||
en("units", "r", 1);
|
||||
|
|
|
|||
Loading…
Reference in New Issue