use image::DynamicImage;
use crate::{geometry::rectangle::Rectangle, rendering::draw::ImageWrapper, text::Text};
pub mod batch;
pub struct ImageBuilder {
content: ImageWrapper,
}
impl Default for ImageBuilder {
fn default() -> Self {
ImageBuilder::new(1000, 1000)
}
}
impl ImageBuilder {
pub fn new(width: u32, height: u32) -> Self {
let content = ImageWrapper::new(DynamicImage::new_rgba8(width, height));
ImageBuilder { content }
}
pub fn from_image(image: DynamicImage) -> Self {
let content = ImageWrapper::new(image);
ImageBuilder { content }
}
pub fn add_text(&mut self, text: Text) -> &mut Self {
self.content.draw(&text);
self
}
pub fn add_rectangle(&mut self, rectangle: Rectangle) -> &mut Self {
self.content.draw(&rectangle);
self
}
pub fn insert_image(&mut self, image: &DynamicImage, x: u32, y: u32) -> &mut Self {
let mut base_image = self.content.get_image().to_rgba8();
let overlay_image = image.to_rgba8();
let (base_width, base_height) = base_image.dimensions();
let (overlay_width, overlay_height) = overlay_image.dimensions();
if x >= base_width || y >= base_height {
return self;
}
let copy_width = std::cmp::min(overlay_width, base_width - x);
let copy_height = std::cmp::min(overlay_height, base_height - y);
if copy_width == 0 || copy_height == 0 {
return self;
}
let base_buffer = base_image.as_mut();
let overlay_buffer = overlay_image.as_raw();
for row in 0..copy_height {
let base_y = y + row;
let overlay_y = row;
let base_row_start = (base_y * base_width + x) as usize * 4;
let overlay_row_start = (overlay_y * overlay_width) as usize * 4;
for col in 0..copy_width {
let base_idx = base_row_start + (col as usize * 4);
let overlay_idx = overlay_row_start + (col as usize * 4);
let overlay_alpha = overlay_buffer[overlay_idx + 3];
if overlay_alpha == 0 {
continue;
}
if overlay_alpha == 255 {
base_buffer[base_idx..base_idx + 4]
.copy_from_slice(&overlay_buffer[overlay_idx..overlay_idx + 4]);
continue;
}
let alpha = overlay_alpha as u32;
let inv_alpha = 255 - alpha;
base_buffer[base_idx] = ((overlay_buffer[overlay_idx] as u32 * alpha +
base_buffer[base_idx] as u32 * inv_alpha) / 255) as u8;
base_buffer[base_idx + 1] = ((overlay_buffer[overlay_idx + 1] as u32 * alpha +
base_buffer[base_idx + 1] as u32 * inv_alpha) / 255) as u8;
base_buffer[base_idx + 2] = ((overlay_buffer[overlay_idx + 2] as u32 * alpha +
base_buffer[base_idx + 2] as u32 * inv_alpha) / 255) as u8;
base_buffer[base_idx + 3] = std::cmp::max(overlay_alpha, base_buffer[base_idx + 3]);
}
}
self.content = ImageWrapper::new(DynamicImage::ImageRgba8(base_image));
self
}
pub fn insert_image_opaque(&mut self, image: &DynamicImage, x: u32, y: u32) -> &mut Self {
let mut base_image = self.content.get_image().to_rgba8();
let overlay_image = image.to_rgba8();
let (base_width, base_height) = base_image.dimensions();
let (overlay_width, overlay_height) = overlay_image.dimensions();
if x >= base_width || y >= base_height {
return self;
}
let copy_width = std::cmp::min(overlay_width, base_width - x);
let copy_height = std::cmp::min(overlay_height, base_height - y);
if copy_width == 0 || copy_height == 0 {
return self;
}
let base_buffer = base_image.as_mut();
let overlay_buffer = overlay_image.as_raw();
for row in 0..copy_height {
let base_y = y + row;
let overlay_y = row;
let base_start = (base_y * base_width + x) as usize * 4;
let base_end = base_start + (copy_width as usize * 4);
let overlay_start = (overlay_y * overlay_width) as usize * 4;
let overlay_end = overlay_start + (copy_width as usize * 4);
base_buffer[base_start..base_end]
.copy_from_slice(&overlay_buffer[overlay_start..overlay_end]);
}
self.content = ImageWrapper::new(DynamicImage::ImageRgba8(base_image));
self
}
pub fn insert_images(&mut self, images: &[(DynamicImage, u32, u32)]) -> &mut Self {
for (image, x, y) in images {
self.insert_image(image, *x, *y);
}
self
}
pub fn get_image(&self) -> DynamicImage {
self.content.get_image().clone()
}
pub fn save_image(&self, path: &str) {
self.content.save_image(path).unwrap();
}
}
impl ImageBuilder {
pub unsafe fn insert_image_unchecked(&mut self, image: &DynamicImage, x: u32, y: u32) -> &mut Self {
let mut base_image = self.content.get_image().to_rgba8();
let overlay_image = image.to_rgba8();
let (base_width, _) = base_image.dimensions();
let (overlay_width, overlay_height) = overlay_image.dimensions();
let base_buffer = base_image.as_mut();
let overlay_buffer = overlay_image.as_raw();
for row in 0..overlay_height {
let base_row_start = ((y + row) * base_width + x) as usize * 4;
let overlay_row_start = (row * overlay_width) as usize * 4;
let row_bytes = (overlay_width as usize) * 4;
unsafe { std::ptr::copy_nonoverlapping(
overlay_buffer.as_ptr().add(overlay_row_start),
base_buffer.as_mut_ptr().add(base_row_start),
row_bytes
) };
}
self.content = ImageWrapper::new(DynamicImage::ImageRgba8(base_image));
self
}
}