diff --git a/crates/doc/src/live.rs b/crates/doc/src/live.rs index fd7886786..8b839c788 100644 --- a/crates/doc/src/live.rs +++ b/crates/doc/src/live.rs @@ -587,12 +587,18 @@ fn even_steps(n: u32) -> Vec { (0..=n).map(|i| i as f64 / n as f64).collect() } +/// The number of points of a `(rows+1)×(cols+1)` grid, `None` past `usize`: counts come from +/// files, and u32::MAX rows and columns (65,535 on 32-bit targets) would wrap to an empty grid. +fn grid_len(rows: u32, cols: u32) -> Option { + (rows as usize).checked_add(1)?.checked_mul((cols as usize).checked_add(1)?) +} + impl GradientMesh { pub fn idx(&self, r: usize, c: usize) -> usize { r * (self.cols as usize + 1) + c } pub fn is_valid(&self) -> bool { - self.rows >= 1 && self.cols >= 1 && self.points.len() == (self.rows as usize + 1) * (self.cols as usize + 1) + self.rows >= 1 && self.cols >= 1 && grid_len(self.rows, self.cols) == Some(self.points.len()) } /// A 1×1 mesh from its four corners (bottom-left, top-left, top-right, bottom-right in u/v @@ -1158,7 +1164,7 @@ fn surface_of<'a>(kind: &'a EnvelopeKind, src: Rect, frame: Affine) -> Box Vec<[Vec2; 4]> /// the smooth ones, see [`smooth_handles`]) as a gradient mesh with black points: what the /// canvas draws and the Mesh tool and Direct Selection edit. `None` for a malformed grid. pub fn envelope_grid(rows: u32, cols: u32, points: &[Point], handles: &[[Vec2; 4]]) -> Option { - // (Counts come from files: no overflow on 32-bit targets.) - if rows == 0 || cols == 0 || points.len() as u64 != (u64::from(rows) + 1) * (u64::from(cols) + 1) { + if rows == 0 || cols == 0 || grid_len(rows, cols) != Some(points.len()) { return None; } let smooth; @@ -1829,6 +1834,20 @@ mod tests { assert!((p.x - 37.0).abs() < 1e-6 && (p.y - 81.0).abs() < 1e-6, "{p:?}"); } + /// A file's rows and columns can be anything: a point count too large to compute is no match + /// for the points (u32::MAX × u32::MAX wrapped to 0 and passed for a grid without points). + #[test] + fn a_grid_too_large_to_count_is_malformed() { + let m = GradientMesh { rows: u32::MAX, cols: u32::MAX, points: vec![] }; + assert!(!m.is_valid()); + assert_eq!((m.lines(), m.outline(), m.quads(8).len()), (PathData::default(), PathData::default(), 0)); + assert!(envelope_grid(u32::MAX, u32::MAX, &[], &[]).is_none()); + // A mesh envelope without handles maps through its points: malformed, it leaves art as it is. + let kind = EnvelopeKind::Mesh { rows: u32::MAX, cols: u32::MAX, points: vec![], handles: vec![] }; + let p = Point::new(25.0, 50.0); + assert_eq!(envelope_mapper(&kind, Rect::new(0.0, 0.0, 100.0, 100.0))(p), p); + } + #[test] fn mesh_insert_and_remove_lines() { let mut m =