use image::{DynamicImage, ImageResult, Rgb, RgbImage};
use imageproc::{
drawing::{draw_filled_rect, draw_hollow_rect},
rect::Rect,
};
use rusttype::Scale;
use crate::{geometry::Orientation, words::Font};
pub struct Image {
img: RgbImage,
bg_color: Rgb<u8>,
width: u32,
height: u32,
}
impl Image {
pub fn new(width: u32, height: u32, bg_color: Option<Rgb<u8>>) -> Self {
let mut image = Image {
img: DynamicImage::new_rgb8(width, height).to_rgb8(),
bg_color: match bg_color {
Some(color) => color,
None => Rgb([255, 255, 255]),
},
width,
height,
};
image.draw_filled_rect(0, 0, width, height, image.bg_color);
image
}
pub fn save(&self, filename: &str) -> ImageResult<()> {
self.img.save(filename)
}
pub fn draw_filled_rect(&mut self, x: i32, y: i32, w: u32, h: u32, color: Rgb<u8>) {
let rect = Rect::at(x, y).of_size(w, h);
self.img = draw_filled_rect(&self.img, rect, color)
}
pub fn draw_rect(&mut self, x: i32, y: i32, w: u32, h: u32, color: Rgb<u8>) {
let rect = Rect::at(x, y).of_size(w, h);
self.img = draw_hollow_rect(&self.img, rect, color)
}
pub fn draw_text(
&mut self,
pos: (i32, i32),
text: &str,
font: &Font,
scale: Scale,
color: Rgb<u8>,
orientation: &Orientation,
) {
let data = font.get_glyph_data(text, scale);
let r = data
.bounding_box(orientation)
.displace(pos.0 as isize, pos.1 as isize)
.unwrap();
let v_metrics = font.get_metrics(scale);
for glyph in data.glyphs {
if let Some(bounding_box) = glyph.pixel_bounding_box() {
glyph.draw(|x, y, v| {
let c = Self::alpha_blend(self.bg_color, color, v);
let new_x = match orientation {
Orientation::Horizontal => x + bounding_box.min.x as u32 + r.pos().0,
Orientation::Vertical => {
(bounding_box.min.y
+ r.pos().0 as i32
+ r.size().0 as i32
+ v_metrics.descent as i32
+ y as i32) as u32
}
};
let new_y = match orientation {
Orientation::Horizontal => {
(y as i32
+ bounding_box.min.y
+ r.pos().1 as i32
+ r.size().1 as i32
+ v_metrics.descent as i32) as u32
}
Orientation::Vertical => {
(-bounding_box.min.x + r.pos().1 as i32 + r.size().1 as i32 - x as i32)
as u32
}
};
if (new_x < self.width) && (new_y < self.height) {
self.img.put_pixel(new_x, new_y, c)
}
});
}
}
}
fn alpha_blend(bg: Rgb<u8>, fg: Rgb<u8>, percentage: f32) -> Rgb<u8> {
fn ab(bg: f32, fg: f32, percentage: f32) -> f32 {
(fg * percentage) + (bg * (1.0 - percentage))
}
Rgb([
ab(bg.0[0] as f32, fg.0[0] as f32, percentage) as u8,
ab(bg.0[1] as f32, fg.0[1] as f32, percentage) as u8,
ab(bg.0[2] as f32, fg.0[2] as f32, percentage) as u8,
])
}
}