diff --git a/src/drawing/rect.rs b/src/drawing/rect.rs index ee2c99ea..af75a785 100644 --- a/src/drawing/rect.rs +++ b/src/drawing/rect.rs @@ -52,12 +52,17 @@ pub fn draw_filled_rect_mut(canvas: &mut C, rect: Rect, color: C::Pixel) where C: Canvas, { + if canvas.width() == 0 || canvas.height() == 0 { + return; + } let canvas_bounds = Rect::at(0, 0).of_size(canvas.width(), canvas.height()); if let Some(intersection) = canvas_bounds.intersect(rect) { for dy in 0..intersection.height() { for dx in 0..intersection.width() { let x = intersection.left() as u32 + dx; let y = intersection.top() as u32 + dy; + debug_assert!(x < canvas.width()); + debug_assert!(y < canvas.height()); canvas.draw_pixel(x, y, color); } } @@ -139,6 +144,31 @@ mod tests { } } +#[cfg(not(miri))] +#[cfg(test)] +mod proptests { + use super::*; + use crate::{proptest_utils::arbitrary_image, rect::Rect}; + use image::Luma; + use proptest::prelude::*; + + proptest! { + #[test] + fn proptest_draw_filled_rect_luma( + image in arbitrary_image::>(0..50, 0..50), + (x, y) in (-50..50, -50..50), + (w, h) in (1..50u32, 1..50u32), + color in 0..255u8, + ) { + let rect = Rect::at(x, y).of_size(w, h); + let color = Luma([color]); + + let out = draw_filled_rect(&image, rect, color); + assert_eq!(out.dimensions(), image.dimensions()); + } + } +} + #[cfg(not(miri))] #[cfg(test)] mod benches { diff --git a/src/rect.rs b/src/rect.rs index 4c644036..7cff3cfa 100644 --- a/src/rect.rs +++ b/src/rect.rs @@ -1,7 +1,5 @@ //! Basic manipulation of rectangles. -use std::cmp; - /// A rectangular region of non-zero width and height. /// # Examples /// ``` @@ -62,14 +60,14 @@ impl Rect { /// /// See the [struct-level documentation](Rect) for examples. pub fn bottom(&self) -> i32 { - self.top + (self.height as i32) - 1 + self.top + i32::try_from(self.height).unwrap() - 1 } /// Greatest x-coordinate reached by rect. /// /// See the [struct-level documentation](Rect) for examples. pub fn right(&self) -> i32 { - self.left + (self.width as i32) - 1 + self.left + i32::try_from(self.width).unwrap() - 1 } /// Width of rect. @@ -105,21 +103,18 @@ impl Rect { /// assert_eq!(r.intersect(s), None); /// ``` pub fn intersect(&self, other: Rect) -> Option { - let left = cmp::max(self.left, other.left); - let top = cmp::max(self.top, other.top); - let right = cmp::min(self.right(), other.right()); - let bottom = cmp::min(self.bottom(), other.bottom()); + let left = self.left.max(other.left); + let top = self.top.max(other.top); + let right = self.right().min(other.right()); + let bottom = self.bottom().min(other.bottom()); if right < left || bottom < top { return None; } - Some(Rect { - left, - top, - width: (right - left) as u32 + 1, - height: (bottom - top) as u32 + 1, - }) + let width = u32::try_from(right - left).unwrap() + 1; + let height = u32::try_from(bottom - top).unwrap() + 1; + Some(Rect::at(left, top).of_size(width, height)) } } @@ -189,3 +184,29 @@ mod tests { assert!(!r.contains(10.1f32, 10f32)); } } + +#[cfg(not(miri))] +#[cfg(test)] +mod proptests { + use super::*; + use proptest::prelude::*; + + proptest! { + #[test] + fn proptest_intersect( + (x1, y1) in (-50..50, -50..50), + (w1, h1) in (1..50u32, 1..50u32), + + (x2, y2) in (-50..50, -50..50), + (w2, h2) in (1..50u32, 1..50u32), + ) { + let rect1 = Rect::at(x1, y1).of_size(w1, h1); + let rect2 = Rect::at(x2, y2).of_size(w2, h2); + + if let Some(intersect) = rect1.intersect(rect2) { + assert!(intersect.width() > 0); + assert!(intersect.height() > 0); + }; + } + } +}