use core::cmp::{max, min};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct PixelRect {
pub x0: usize,
pub y0: usize,
pub x1: usize,
pub y1: usize,
}
impl PixelRect {
#[inline]
pub fn new(x0: usize, y0: usize, x1: usize, y1: usize) -> Self {
Self { x0, y0, x1, y1 }
}
#[inline]
pub fn empty() -> Self {
Self::default()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.x0 >= self.x1 || self.y0 >= self.y1
}
#[inline]
pub fn width(&self) -> usize {
self.x1.saturating_sub(self.x0)
}
#[inline]
pub fn height(&self) -> usize {
self.y1.saturating_sub(self.y0)
}
#[inline]
pub fn union(self, other: PixelRect) -> PixelRect {
if self.is_empty() {
return other;
}
if other.is_empty() {
return self;
}
PixelRect {
x0: min(self.x0, other.x0),
y0: min(self.y0, other.y0),
x1: max(self.x1, other.x1),
y1: max(self.y1, other.y1),
}
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Damage {
bbox: PixelRect,
}
impl Damage {
#[inline]
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn add(&mut self, rect: PixelRect) {
self.bbox = self.bbox.union(rect);
}
#[inline]
pub fn add_bounds(&mut self, x_start: usize, y_start: usize, x_end: usize, y_end: usize) {
self.add(PixelRect::new(x_start, y_start, x_end, y_end));
}
#[inline]
pub fn bbox(&self) -> PixelRect {
self.bbox
}
#[inline]
pub fn is_empty(&self) -> bool {
self.bbox.is_empty()
}
#[inline]
pub fn clear(&mut self) {
self.bbox = PixelRect::empty();
}
}
pub struct Canvas<'a> {
pub pixels: &'a mut [u32],
pub width: usize,
pub height: usize,
pub damage: &'a mut Damage,
}
impl<'a> Canvas<'a> {
#[inline]
pub fn new(pixels: &'a mut [u32], width: usize, height: usize, damage: &'a mut Damage) -> Self {
Self {
pixels,
width,
height,
damage,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pixel_rect_union_with_empty_is_other() {
let a = PixelRect::new(10, 20, 30, 40);
let e = PixelRect::empty();
assert_eq!(a.union(e), a);
assert_eq!(e.union(a), a);
}
#[test]
fn pixel_rect_union_two_non_overlapping() {
let a = PixelRect::new(0, 0, 10, 10);
let b = PixelRect::new(50, 60, 70, 80);
assert_eq!(a.union(b), PixelRect::new(0, 0, 70, 80));
}
#[test]
fn damage_starts_empty_and_unions_on_add() {
let mut d = Damage::new();
assert!(d.is_empty());
d.add_bounds(5, 5, 15, 15);
assert_eq!(d.bbox(), PixelRect::new(5, 5, 15, 15));
d.add_bounds(100, 100, 110, 110);
assert_eq!(d.bbox(), PixelRect::new(5, 5, 110, 110));
}
#[test]
fn damage_clear_resets_to_empty() {
let mut d = Damage::new();
d.add_bounds(1, 2, 3, 4);
assert!(!d.is_empty());
d.clear();
assert!(d.is_empty());
}
}