use std::{collections::HashMap, fs::File};
pub use image::imageops::FilterType;
use image::{
codecs::png::PngEncoder,
imageops::{crop, overlay, resize},
ImageBuffer, ImageEncoder, Rgba,
};
use imageproc::{
drawing::{draw_filled_rect_mut, draw_text_mut, text_size},
rect as procRect,
};
use rusttype::Font;
use crate::{
colors::Color,
picture::{self, Picture},
rect::{self, Rect},
text::{self, Text},
};
#[derive(Clone)]
pub enum Element {
Text(Text),
Rect(Rect),
Picture(Picture),
}
#[derive(Clone)]
pub struct Image<'a> {
background: Color,
size: (u32, u32),
fonts: HashMap<&'a str, Font<'a>>,
elements: Vec<Element>,
}
impl<'a> Image<'a> {
pub fn new(width: u32, height: u32, background: Color) -> Image<'a> {
let default_font = Vec::from(include_bytes!("Roboto-Regular.ttf") as &[u8]);
let default_font = Font::try_from_vec(default_font)
.expect("Fail to load the default font \"Roboto-Regular.ttf\"");
Image {
background,
size: (width, height),
fonts: HashMap::from([("default", default_font)]),
elements: Vec::new(),
}
}
pub fn add_custom_font(&mut self, name: &'a str, font: Vec<u8>) {
let font = Font::try_from_vec(font).expect(&format!("Fail to load the font \"{}\"", name));
self.fonts.insert(name, font);
}
pub fn add_picture(&mut self, picture: Picture) {
self.elements.push(Element::Picture(picture));
}
pub fn add_text(&mut self, text: Text) {
self.elements.push(Element::Text(text));
}
pub fn text_size(&mut self, text: &Text) -> (i32, i32) {
let t = text::extract(&text);
let font = self.fonts.get(t.font_name).expect(&format!("Unable to load the \"{}\" font, please verify that the name is correct or that it was loaded using the \"add_custom_font\" method.", t.font_name));
text_size(t.scale, font, &t.content)
}
pub fn add_rect(&mut self, rect: Rect) {
self.elements.push(Element::Rect(rect));
}
pub fn save(&mut self, file_name: &str) {
let mut image = ImageBuffer::from_pixel(self.size.0, self.size.1, Rgba(self.background));
for element in self.elements.iter() {
match element {
Element::Picture(element) => {
let p = picture::extract(element);
let mut pic = p.img.to_rgba8();
if let Some(values) = p.resize {
pic = resize(&mut pic, values.nwidth, values.nheight, values.filter)
}
if let Some(values) = p.crop {
pic = crop(&mut pic, values.x, values.y, values.width, values.height)
.to_image();
}
overlay(&mut image, &pic, p.x, p.y);
}
Element::Text(element) => {
let t = text::extract(&element);
let font = self.fonts.get(t.font_name).expect(&format!("Unable to load the \"{}\" font, please verify that the name is correct or that it was loaded using the \"add_custom_font\" method.", t.font_name));
let mut text_image =
ImageBuffer::from_pixel(self.size.0, self.size.1, Rgba([0, 0, 0, 0]));
draw_text_mut(&mut text_image, t.color, 0, 0, t.scale, font, t.content);
overlay(&mut image, &text_image, t.x as i64, t.y as i64);
}
Element::Rect(element) => {
let r = rect::extract(element);
let mut rect_image =
ImageBuffer::from_pixel(r.width, r.height, Rgba([0, 0, 0, 0]));
draw_filled_rect_mut(
&mut rect_image,
procRect::Rect::at(0, 0).of_size(r.width, r.height),
r.color,
);
overlay(&mut image, &rect_image, r.x as i64, r.y as i64);
}
}
}
let file = File::create(file_name).expect(&format!(
"It was not possible to create the file \"{}\" because the file path does not exist.",
file_name
));
let encoder = PngEncoder::new(file);
encoder
.write_image(
&image,
image.width(),
image.height(),
image::ColorType::Rgba8,
)
.unwrap();
}
}