use denise::Rect;
use crate::blend::{Paint, blend_span};
use crate::canvas::Canvas;
use crate::painter::Painter;
impl Canvas<'_> {
pub fn fill_rect(&mut self, rect: Rect, color: impl Into<Paint>) {
let paint = color.into();
if paint.is_invisible() {
return;
}
let Some(r) = self.visible(rect) else {
return;
};
for y in r.y..r.bottom() {
if let Some(span) = self.row_span(y, r.x, r.right()) {
blend_span(span, paint);
}
}
}
pub fn fill_rects(&mut self, rects: &[Rect], color: impl Into<Paint>) {
Painter::fill_rects(self, rects, color.into());
}
pub fn stroke_rect(&mut self, rect: Rect, thickness: i32, color: impl Into<Paint>) {
Painter::stroke_rect(self, rect, thickness, color.into());
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::testing::TestCanvas;
use denise::Color;
#[test]
fn fill_clips_to_the_canvas() {
let mut t = TestCanvas::new(8, 8);
t.canvas().fill_rect(Rect::new(-4, -4, 8, 8), Color::WHITE);
assert_eq!(t.at(0, 0), 0xFFFF_FFFF);
assert_eq!(t.at(3, 3), 0xFFFF_FFFF);
assert_eq!(t.at(4, 0), 0);
assert_eq!(t.at(0, 4), 0);
}
#[test]
fn fill_never_touches_stride_padding() {
let mut t = TestCanvas::with_stride(10, 4, 16);
t.canvas().fill_rect(Rect::new(0, 0, 10, 4), Color::WHITE);
for row in t.pixels().chunks(16) {
assert!(row[..10].iter().all(|&p| p == 0xFFFF_FFFF));
assert!(row[10..].iter().all(|&p| p == 0));
}
}
#[test]
fn wholly_offscreen_fill_is_a_noop() {
let mut t = TestCanvas::new(8, 8);
t.canvas()
.fill_rect(Rect::new(100, 100, 8, 8), Color::WHITE);
assert!(t.pixels().iter().all(|&p| p == 0));
}
#[test]
fn half_alpha_twice_is_not_full_alpha() {
let mut t = TestCanvas::new(4, 4);
t.canvas()
.fill_rect(Rect::new(0, 0, 4, 4), Color::rgba(255, 255, 255, 128));
let once = t.at(0, 0);
t.canvas()
.fill_rect(Rect::new(0, 0, 4, 4), Color::rgba(255, 255, 255, 128));
let twice = t.at(0, 0);
assert!(twice > once, "second coat must lighten further");
assert!(twice < 0xFFFF_FFFF, "two half coats must not reach opaque");
}
#[test]
fn stroke_corners_are_not_composited_twice() {
let mut t = TestCanvas::new(16, 16);
t.canvas()
.stroke_rect(Rect::new(0, 0, 16, 16), 2, Color::rgba(255, 255, 255, 128));
assert_eq!(t.at(0, 0), t.at(8, 0));
assert_eq!(t.at(0, 0), t.at(0, 8));
}
#[test]
fn stroke_leaves_the_interior_alone() {
let mut t = TestCanvas::new(16, 16);
t.canvas()
.stroke_rect(Rect::new(2, 2, 12, 12), 3, Color::WHITE);
assert_eq!(t.at(2, 2), 0xFFFF_FFFF);
assert_eq!(t.at(4, 4), 0xFFFF_FFFF);
assert_eq!(t.at(5, 5), 0, "interior must be untouched");
assert_eq!(t.at(1, 1), 0, "outside must be untouched");
}
#[test]
fn stroke_thicker_than_the_rect_becomes_a_fill() {
let mut t = TestCanvas::new(8, 8);
t.canvas()
.stroke_rect(Rect::new(1, 1, 4, 4), 9, Color::WHITE);
assert_eq!(t.at(2, 2), 0xFFFF_FFFF);
assert_eq!(t.at(0, 0), 0);
}
}