diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index 4e5e73850..b12eb1efe 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -424,6 +424,17 @@ fn degenerate_crops_render_with_spots_and_masks() { } } +/// `crop.set` with a huge rect shrank it by a scale of 0 (`inf * 0`) and stored a NaN crop. +#[test] +fn crop_set_huge_rect_stays_finite() { + for rect in [[0.0, 0.0, 1e308, 1e308], [-1e308, -1e308, 1e308, 1e308]] { + let mut s = demo(); + let _ = s.execute("crop.set", &json!({"rect": rect})); + let r = active_dev(&s).crop.geometry.rect; + assert!([r.x0, r.y0, r.x1, r.y1].iter().all(|v| v.is_finite() && (-1e-6..=1.0 + 1e-6).contains(v)), "{rect:?}: {r:?}"); + } +} + /// `mask.move` with a delta near `i64::MAX` overflowed `index + delta` (a panic wherever overflow /// checks are on); extreme deltas and indices now move the mask to the bottom or the top. #[test] diff --git a/crates/geom/src/crop.rs b/crates/geom/src/crop.rs index 857ea19b9..e6906a42b 100644 --- a/crates/geom/src/crop.rs +++ b/crates/geom/src/crop.rs @@ -77,7 +77,14 @@ impl CropGeometry { let s = max_inscribed_scale(w, h, self.angle, r.width(), r.height()); let nr = Rect::from_center(c, r.width() * s, r.height() * s); let _ = best; - CropGeometry { rect: nr.scale(1.0 / w, 1.0 / h), angle: self.angle } + let out = CropGeometry { rect: nr.scale(1.0 / w, 1.0 / h), angle: self.angle }; + if [out.rect.x0, out.rect.y0, out.rect.x1, out.rect.y1].iter().all(|v| v.is_finite()) { + return out; + } + // A rect too large for pixel maths (`inf * 0` above): the largest crop of its aspect that + // fits, else of the image's own. + let aspect = Some(self.rect.width() / self.rect.height() * (w / h)).filter(|a| a.is_finite() && *a > 0.0); + crop_fit_angle(w, h, self.angle, aspect) } /// This crop turned to `angle_deg` on a `w × h` image: kept as it is when it still fits inside @@ -211,4 +218,16 @@ mod tests { let p = t.apply(Point::new(0.0, 0.0)); assert!((p.x - 25.0).abs() < 1e-9 && (p.y - 25.0).abs() < 1e-9); } + + #[test] + fn constrained_huge_rect_is_finite_and_inside() { + for rect in [Rect::new(0.0, 0.0, 1e308, 1e308), Rect::new(-1e308, -1e308, 1e308, 1e308), Rect::new(0.0, 0.0, 1e308, 0.5)] { + for angle in [0.0, 30.0] { + let c = CropGeometry { rect, angle }.constrained(400.0, 300.0); + let r = c.rect; + assert!([r.x0, r.y0, r.x1, r.y1].iter().all(|v| v.is_finite()), "{rect:?} {angle}: {r:?}"); + assert!(c.is_within_image(400.0, 300.0), "{rect:?} {angle}: {r:?}"); + } + } + } }