diff --git a/crates/aep/src/export/fx_defs.rs b/crates/aep/src/export/fx_defs.rs index cc80eaaaa..90db58758 100644 --- a/crates/aep/src/export/fx_defs.rs +++ b/crates/aep/src/export/fx_defs.rs @@ -746,10 +746,6 @@ mod tests { ("ec.color.changecolor", "hueTransform", 3480), ("ec.time.pixelmotionblur", "shutterAngle", 3481), ("ec.time.pixelmotionblur", "shutterSamples", 3482), - ("ec.noise.noisehls", "hue", 3483), - ("ec.noise.noisehls", "lightness", 3483), - ("ec.noise.noisehls", "saturation", 3483), - ("ec.noise.noisehls", "grainSize", 3484), ("ec.noise.fractal", "subSettings/subInfluence", 3485), ("ec.noise.fractal", "subSettings/subScaling", 3486), ("ec.distort.ccpowerpin", "expandTop", 3492), diff --git a/crates/effects/src/noise.rs b/crates/effects/src/noise.rs index 09217f831..246910cd5 100644 --- a/crates/effects/src/noise.rs +++ b/crates/effects/src/noise.rs @@ -766,10 +766,12 @@ pub fn specs() -> Vec { let hls_common = || { vec![ p("noise", "Noise", Value::Enum(0), popup(&["Uniform", "Squared", "Grain"])), - p("hue", "Hue", num(0.0), pct()), - p("lightness", "Lightness", num(0.0), pct()), - p("saturation", "Saturation", num(0.0), pct()), - p("grainSize", "Grain Size", num(1.0), slider(0.1, 100.0, 0.5, 10.0, 2)), + // After Effects' valid ranges (#3483, #3484): the amounts go to 1000 %, Grain Size + // down to 0 (the renderer floors it at 0.1 px) and keeps its wider maximum. + p("hue", "Hue", num(0.0), slider(0.0, 1000.0, 0.0, 100.0, 1)), + p("lightness", "Lightness", num(0.0), slider(0.0, 1000.0, 0.0, 100.0, 1)), + p("saturation", "Saturation", num(0.0), slider(0.0, 1000.0, 0.0, 100.0, 1)), + p("grainSize", "Grain Size", num(1.0), slider(0.0, 100.0, 0.5, 10.0, 2)), ] }; let mut hls = hls_common(); @@ -967,6 +969,28 @@ mod tests { assert_eq!(still.img, img); } + /// #3483, #3484: amounts above 100 % keep adding noise, and Grain Size 0 renders (floored at + /// 0.1 px) with finite output, for both effects. + #[test] + fn noise_hls_takes_after_effects_ranges() { + let mut img = Image::new(16, 16); + for (i, p) in img.data.iter_mut().enumerate() { + *p = premul([0.6, 0.3, (i % 7) as f32 / 7.0], 1.0); + } + for id in ["ec.noise.noisehls", "ec.noise.noisehlsauto"] { + let at = |amount: f64, grain: f64| { + let vals = + [("noise", Value::Enum(2)), ("hue", num(amount)), ("lightness", num(amount)), ("saturation", num(amount)), ("grainSize", num(grain))]; + run(id, &vals, img.clone(), 0.0).img + }; + let (low, high) = (at(100.0, 1.0), at(1000.0, 1.0)); + assert_ne!(low, high, "{id}"); + for out in [high, at(1000.0, 0.0)] { + assert!(out.data.iter().flatten().all(|v| v.is_finite() && (0.0..=1.0).contains(v)), "{id}"); + } + } + } + #[test] fn add_grain_keeps_flat_mean() { let out = run("ec.noise.addgrain", &[("tweaking/intensity", num(2.0)), ("application/blendingMode", Value::Enum(2))], gray(64, 64, 0.5), 0.0); diff --git a/crates/engine/src/interchange/keyframe_text_effects.rs b/crates/engine/src/interchange/keyframe_text_effects.rs index 94d81afcc..7b0658f61 100644 --- a/crates/engine/src/interchange/keyframe_text_effects.rs +++ b/crates/engine/src/interchange/keyframe_text_effects.rs @@ -318,6 +318,15 @@ pub(crate) const UNITS: &[(&str, &str, &str)] = &[ ("ec.matte.rotobrush", "refineEdgeMatte/shiftEdge", "percent"), ("ec.matte.rotobrush", "refineEdgeMatte/decontamination/increaseDecontaminationRadius", ""), ("ec.matte.simplechoker", "chokeMatte", "pixels"), + // Their ranges were 0..100 (#3483, #3484). + ("ec.noise.noisehls", "hue", "percent"), + ("ec.noise.noisehls", "lightness", "percent"), + ("ec.noise.noisehls", "saturation", "percent"), + ("ec.noise.noisehls", "grainSize", ""), + ("ec.noise.noisehlsauto", "hue", "percent"), + ("ec.noise.noisehlsauto", "lightness", "percent"), + ("ec.noise.noisehlsauto", "saturation", "percent"), + ("ec.noise.noisehlsauto", "grainSize", ""), ("ec.noise.fractal", "transform/scale", "percent"), ("ec.noise.fractal", "transform/scaleWidth", "percent"), ("ec.noise.fractal", "transform/scaleHeight", "percent"), diff --git a/crates/engine/src/tests/effect_ranges.rs b/crates/engine/src/tests/effect_ranges.rs index cb4d4ea24..9c0c336ec 100644 --- a/crates/engine/src/tests/effect_ranges.rs +++ b/crates/engine/src/tests/effect_ranges.rs @@ -41,6 +41,14 @@ const ROWS: &[(u32, &str, &str, f64, f64)] = &[ (3491, "ec.color.levelsic", "alpha/alphaGamma", 0.0, 10.0), (3478, "ec.generate.fourcolor", "blend", 1.0, 10000.0), (3479, "ec.generate.fourcolor", "jitter", 0.0, 500.0), + (3483, "ec.noise.noisehls", "hue", 0.0, 1000.0), + (3483, "ec.noise.noisehls", "lightness", 0.0, 1000.0), + (3483, "ec.noise.noisehls", "saturation", 0.0, 1000.0), + (3484, "ec.noise.noisehls", "grainSize", 0.0, 100.0), + (3483, "ec.noise.noisehlsauto", "hue", 0.0, 1000.0), + (3483, "ec.noise.noisehlsauto", "lightness", 0.0, 1000.0), + (3483, "ec.noise.noisehlsauto", "saturation", 0.0, 1000.0), + (3484, "ec.noise.noisehlsauto", "grainSize", 0.0, 100.0), ]; fn path(effect: &str, param: &str) -> String { diff --git a/crates/engine/src/tests/keyframe_text.rs b/crates/engine/src/tests/keyframe_text.rs index 81d9487d9..a299899eb 100644 --- a/crates/engine/src/tests/keyframe_text.rs +++ b/crates/engine/src/tests/keyframe_text.rs @@ -497,4 +497,12 @@ fn widened_effect_ranges_keep_their_keyframe_text_units() { let text = copy_props(&mut s, l, &["effects/#1/blend", "effects/#1/jitter"]); assert_eq!(unit_of(&text, "Blend"), "percent"); assert_eq!(unit_of(&text, "Jitter"), "percent"); + for fx in ["ec.noise.noisehls", "ec.noise.noisehlsauto"] { + let (mut s, l) = session(); + s.execute("effect.apply", json!({"layer": l.0, "effect": fx})).unwrap(); + let text = copy_props(&mut s, l, &["effects/#1/hue", "effects/#1/lightness", "effects/#1/saturation", "effects/#1/grainSize"]); + for (name, unit) in [("Hue", "percent"), ("Lightness", "percent"), ("Saturation", "percent"), ("Grain Size", "")] { + assert_eq!(unit_of(&text, name), unit, "{fx} {name}"); + } + } } diff --git a/crates/gpu/src/tests/fx_noise.rs b/crates/gpu/src/tests/fx_noise.rs index 7d13004ee..006128c73 100644 --- a/crates/gpu/src/tests/fx_noise.rs +++ b/crates/gpu/src/tests/fx_noise.rs @@ -164,6 +164,8 @@ fn noise_alpha_and_hls() { effect_case("ec.noise.noisehls", &[("hue", n(30.0)), ("lightness", n(20.0)), ("saturation", n(40.0))]); effect_case("ec.noise.noisehls", &[("noise", e(1)), ("lightness", n(50.0)), ("noisePhase", n(200.0))]); effect_case("ec.noise.noisehls", &[("noise", e(2)), ("hue", n(60.0)), ("saturation", n(25.0)), ("grainSize", n(3.0))]); + // #3483, #3484: the ends of After Effects' ranges. + effect_case("ec.noise.noisehls", &[("noise", e(2)), ("hue", n(1000.0)), ("lightness", n(1000.0)), ("saturation", n(1000.0)), ("grainSize", n(0.0))]); effect_case("ec.noise.noisehlsauto", &[("noise", e(0)), ("hue", n(20.0)), ("lightness", n(30.0)), ("noiseAnimationSpeed", n(7.0))]); }