use image::DynamicImage;
use rgb::Rgba;
use tracing::debug;
use crate::primitive::layer::{BlendMode, Layer, PixelProvider, Transform};
use crate::rendering::draw::Drawable;
#[derive(Clone, Copy, Debug)]
pub enum BorderType {
Solid,
Dashed,
}
#[derive(Clone, Copy, Debug)]
pub struct Border {
pub thickness: u8,
pub color: Rgba<u8>,
pub r#type: BorderType,
}
impl Default for Border {
fn default() -> Self {
Border {
thickness: 10,
color: Rgba::new(100, 100, 100, 100),
r#type: BorderType::Solid,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct Rectangle {
pub x1: u32,
pub y1: u32,
pub x2: u32,
pub y2: u32,
pub width: u32,
pub height: u32,
pub border: Option<Border>,
pub filled: bool,
pub fill_color: Option<Rgba<u8>>,
}
impl Rectangle {
pub fn new(x1: u32, y1: u32, x2: u32, y2: u32) -> Rectangle {
Rectangle {
x1,
y1,
x2,
y2,
border: Some(Border::default()),
filled: true,
width: x2 - x1,
height: y2 - y1,
fill_color: None,
}
}
pub fn from_xywh(x: u32, y: u32, width: u32, height: u32) -> Rectangle {
Rectangle {
x1: x,
y1: y,
x2: x + width,
y2: y + height,
border: Some(Border::default()),
filled: true,
width,
height,
fill_color: None,
}
}
pub fn filled(&self, fill_color: Rgba<u8>) -> Self {
let border = self.border.unwrap_or_default();
Rectangle {
x1: self.x1,
y1: self.y1,
x2: self.x2,
y2: self.y2,
border: Some(border),
filled: true,
width: self.width,
height: self.height,
fill_color: Some(fill_color),
}
}
pub fn to_layer(&self) -> Layer<Self> {
Layer {
content: *self,
position: (self.x1 as f64, self.y1 as f64),
z_index: 0, opacity: 1.0, blend_mode: BlendMode::Normal, transform: Transform::identity(), visible: true, clip_mask: None, last_render_hash: 0, }
}
}
impl PixelProvider for Rectangle {
fn pixel_at(&self, x: u32, y: u32) -> rgb::Rgba<u8> {
if self.filled {
return self.fill_color.unwrap_or(Rgba::new(0, 0, 0, 255));
}
if let Some(border) = &self.border {
let within_x_bounds = x >= self.x1 && x < self.x2;
let within_y_bounds = y >= self.y1 && y < self.y2;
let on_left_or_right_border = within_y_bounds
&& (x < self.x1 + border.thickness as u32
|| x >= self.x2 - border.thickness as u32);
let on_top_or_bottom_border = within_x_bounds
&& (y < self.y1 + border.thickness as u32
|| y >= self.y2 - border.thickness as u32);
if on_left_or_right_border || on_top_or_bottom_border {
return border.color;
}
}
Rgba::new(0, 0, 0, 0) }
fn width(&self) -> u32 {
self.width
}
fn height(&self) -> u32 {
self.height
}
}
impl Drawable for Rectangle {
fn draw(&self, image: &mut DynamicImage) {
debug!(
"Drawing rectangle at ({}, {}) with dimensions {}x{}",
self.x1, self.y1, self.width, self.height
);
let layer = self.to_layer();
let delta_buffer = layer.collect_changes(None);
debug!(
"Collected {} pixel changes for rendering.",
delta_buffer.changes.len()
);
let mut img = image.to_rgba8();
for change in delta_buffer.changes {
let idx = ((change.y * img.width() + change.x) * 4) as usize;
img.as_mut()[idx..idx + 4].copy_from_slice(&change.color);
}
*image = DynamicImage::ImageRgba8(img);
}
}