use ril::prelude::*; use std::path::Path; pub struct Generate, V: AsRef> { elements: Vec>, base: Image, } impl, V: AsRef> Generate { pub fn new(base: Vec, elements: Vec>) -> Result { Ok(Self { base: 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::open(text.font, 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()) } }