diff --git a/crates/engine/src/cmd/expand.rs b/crates/engine/src/cmd/expand.rs index ee9b582dd..512501d8d 100644 --- a/crates/engine/src/cmd/expand.rs +++ b/crates/engine/src/cmd/expand.rs @@ -747,6 +747,11 @@ pub(crate) fn gradient_sampler(g: &GradientPaint, bounds: Rect, doc_model: Model fn breaks(g: &Gradient, t0: f64, t1: f64, max_gap: f64) -> Vec<(f64, (Color, f32))> { let at = |t: f64| (t, g.sample_keep_model(t as f32)); let mut v = vec![at(t0)]; + // A Perceptual gradient curves between its stops: the mesh takes a column at each of the stops + // its expansion adds, so the linear mix between columns follows that curve. + if !g.method.is_classic() { + v.extend(g.expanded().map(|(o, ..)| f64::from(o)).filter(|o| *o > t0 && *o < t1).map(at)); + } for (i, s) in g.stops.iter().enumerate() { let o = s.offset as f64; if o > t0 && o < t1 { diff --git a/crates/engine/src/tests_objexpand.rs b/crates/engine/src/tests_objexpand.rs index ea92bca1f..48435adbb 100644 --- a/crates/engine/src/tests_objexpand.rs +++ b/crates/engine/src/tests_objexpand.rs @@ -197,6 +197,8 @@ fn the_mesh_option_makes_a_mesh_that_paints_the_gradient() { for gradient in [ json!({"kind": "linear", "angle": 30, "stops": [{"offset": 0, "color": "#ff0000"}, {"offset": 0.4, "color": "#00ff00", "midpoint": 0.3}, {"offset": 1, "color": "#0000ff"}]}), json!({"kind": "radial", "stops": [{"offset": 0, "color": "#ffff00"}, {"offset": 0.5, "color": "#ff00ff"}, {"offset": 1, "color": "#004080"}]}), + // Perceptual: the mesh follows the OKLab curve between the stops, not a straight RGB mix. + json!({"kind": "linear", "method": "perceptual", "stops": [{"offset": 0, "color": "#2050f0"}, {"offset": 1, "color": "#f5d020"}]}), ] { let mut s = session(); gradient_rect(&mut s, Rect::new(30.0, 40.0, 170.0, 160.0), gradient.clone(), false);