use magick_rust::{magick_wand_genesis, MagickError, MagickWand}; use ril::prelude::*; use std::sync::Once; use std::{fs::File, path::Path}; // Used to make sure MagickWand is initialized exactly once. Note that we // do not bother shutting down, we simply exit when we're done. static START: Once = Once::new(); pub struct Template, V: AsRef> { elements: Vec>, base: Image, } impl, V: AsRef> Template { pub fn new(base: Vec, elements: Vec>) -> Result { Ok(Self { base: match Image::::from_bytes_inferred(&base) { Ok(img) => img, _ => { START.call_once(|| { magick_wand_genesis(); }); let mut wand = MagickWand::new(); wand.read_image_blob(&base)?; wand.set_format("png32")?; let base = wand.write_image_blob("png")?; Image::from_bytes_inferred(base)? } }, elements, }) } pub fn produce(self) -> Result, CowError> { let size = self.base.dimensions(); let mut base_image = self.base.with_overlay_mode(OverlayMode::Merge); for element in self.elements { let mut pos = element.position; let mut img: Image = element.into_image()?; if pos.0 < 0 { img.crop(-pos.0 as u32, 0, size.0, size.1); pos.0 = 0; } if pos.1 < 0 { img.crop(0, -pos.1 as u32, size.0, size.1); pos.1 = 0; } base_image.draw(&Paste::new(img.convert()).with_position(pos.0 as u32, pos.1 as u32)); } let mut buf = Vec::::new(); base_image.encode(ImageFormat::Png, &mut buf).unwrap(); Ok(buf) } } pub struct Element, V: AsRef> { position: (i32, i32), dimensions: (u32, u32), media: Media, } impl, V: AsRef> Element { pub fn new(media: Media, position: (i32, i32), dimensions: (u32, u32)) -> Self { Self { position, dimensions, media, } } fn into_image(self) -> Result { match self.media { Media::Image(image) => Ok(Image::from_bytes_inferred(image)?.resized( self.dimensions.0, self.dimensions.1, ResizeAlgorithm::Bicubic, )), Media::Text(text) => Ok(Image::new( self.dimensions.0, self.dimensions.1, Dynamic::Rgba(Rgba::transparent()), ) .with( &TextSegment::new( &Font::from_reader(File::open(text.font).unwrap(), 12.0).unwrap(), text.text, Dynamic::Rgba(Rgba::new( text.fill.0, text.fill.1, text.fill.2, text.fill.3, )), ) .with_size(text.size), )), } } } pub enum Media, V: AsRef> { Text(Text), Image(Vec), } pub struct Text, V: AsRef> { text: T, font: V, size: f32, fill: (u8, u8, u8, u8), } impl, V: AsRef> Text { pub fn new(text: T, font: V, size: f32, fill: (u8, u8, u8, u8)) -> Self { Self { text, font, size, fill, } } } #[derive(Clone, Debug)] pub enum CowError { Magick(String), Other(String), } impl From for CowError { fn from(r: ril::Error) -> Self { CowError::Other(r.to_string()) } } impl From for CowError { fn from(m: MagickError) -> Self { CowError::Magick(m.0.to_owned()) } } #[cfg(test)] mod tests { #[test] fn it_works() { let result = 2 + 2; assert_eq!(result, 4); } }