use ril::prelude::*; pub fn produce(base: &Vec, elements: Vec) -> Result, CowError> { let base = Image::::from_bytes_inferred(base)?; let size = base.dimensions(); let mut base_image = base.with_overlay_mode(OverlayMode::Merge); for element in 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 { position: (i32, i32), dimensions: (u32, u32), media: Media, } impl 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 { // TODO: allow changing treatment of image 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_bytes(&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 { Text(Text), Image((Box>, ImageMode)), } pub struct Text { text: String, font: Box>, size: f32, fill: (u8, u8, u8, u8), } pub enum ImageMode { Stretch, Fill, Crop, } impl Text { pub fn new(text: String, font: Box>, 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()) } }