From e7e5236b80d21d768e4246f0c0e27b1f54f00345 Mon Sep 17 00:00:00 2001 From: emilis Date: Wed, 2 Nov 2022 23:24:18 +0000 Subject: [PATCH] Finish image error handling w/ magick --- cowgen/Cargo.toml | 1 + cowgen/src/lib.rs | 69 +++++++++++++++++++++++++++++++--------------- cowmic/src/main.rs | 33 +++++++++++++--------- 3 files changed, 68 insertions(+), 35 deletions(-) diff --git a/cowgen/Cargo.toml b/cowgen/Cargo.toml index 59fad44..463069b 100644 --- a/cowgen/Cargo.toml +++ b/cowgen/Cargo.toml @@ -8,3 +8,4 @@ edition = "2021" [dependencies] ril = { version = "0", features = ["all"] } +magick_rust = { version = "0.16" } diff --git a/cowgen/src/lib.rs b/cowgen/src/lib.rs index 394861c..eb56ff6 100644 --- a/cowgen/src/lib.rs +++ b/cowgen/src/lib.rs @@ -1,27 +1,43 @@ +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>, - dimensions: (u32, u32), - base: Image, + base: Image, } impl, V: AsRef> Template { pub fn new(base: Vec, elements: Vec>) -> Result { - let base: Image = Image::from_bytes_inferred(base)?; Ok(Self { - dimensions: base.dimensions(), + 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, - base, }) } - pub fn produce(mut self) -> Vec { + 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(); + 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; @@ -31,13 +47,12 @@ impl, V: AsRef> Template { pos.1 = 0; } - self.base - .draw(&Paste::new(img).with_position(pos.0 as u32, pos.1 as u32)); + base_image.draw(&Paste::new(img.convert()).with_position(pos.0 as u32, pos.1 as u32)); } let mut buf = Vec::::new(); - self.base.encode(ImageFormat::Png, &mut buf).unwrap(); + base_image.encode(ImageFormat::Png, &mut buf).unwrap(); - buf + Ok(buf) } } @@ -55,17 +70,15 @@ impl, V: AsRef> Element { media, } } -} -impl, V: AsRef> Into for Element { - fn into(self) -> Image { + fn into_image(self) -> Result { match self.media { - Media::Image(image) => image.resized( + Media::Image(image) => Ok(Image::from_bytes_inferred(image)?.resized( self.dimensions.0, self.dimensions.1, ResizeAlgorithm::Bicubic, - ), - Media::Text(text) => Image::new( + )), + Media::Text(text) => Ok(Image::new( self.dimensions.0, self.dimensions.1, Dynamic::Rgba(Rgba::transparent()), @@ -74,28 +87,33 @@ impl, V: AsRef> Into for Element { &TextSegment::new( &Font::from_reader(File::open(text.font).unwrap(), 12.0).unwrap(), text.text, - Dynamic::Rgb(Rgb::new(text.fill.0, text.fill.1, text.fill.2)), + 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(Image), + Image(Vec), } pub struct Text, V: AsRef> { text: T, font: V, size: f32, - fill: (u8, u8, u8), + fill: (u8, u8, u8, u8), } impl, V: AsRef> Text { - pub fn new(text: T, font: V, size: f32, fill: (u8, u8, u8)) -> Self { + pub fn new(text: T, font: V, size: f32, fill: (u8, u8, u8, u8)) -> Self { Self { text, font, @@ -107,6 +125,7 @@ impl, V: AsRef> Text { #[derive(Clone, Debug)] pub enum CowError { + Magick(String), Other(String), } @@ -116,6 +135,12 @@ impl From for CowError { } } +impl From for CowError { + fn from(m: MagickError) -> Self { + CowError::Magick(m.0.to_owned()) + } +} + #[cfg(test)] mod tests { #[test] diff --git a/cowmic/src/main.rs b/cowmic/src/main.rs index 0bdc03b..ec49f0a 100644 --- a/cowmic/src/main.rs +++ b/cowmic/src/main.rs @@ -4,20 +4,27 @@ use cowgen::{CowError, Element, Text}; fn main() -> Result<(), CowError> { let out = cowgen::Template::new( - include_bytes!("cow2.png").to_vec(), - vec![Element::new( - cowgen::Media::Text(Text::new( - "Hello", - "/usr/share/fonts/TTF/OpenSans-ExtraBold.ttf", - 12.0, - (0, 0, 0), - )), - (0, 0), - (64, 64), - )], + include_bytes!("cow.png").to_vec(), + vec![ + Element::new( + cowgen::Media::Text(Text::new( + "Hello", + "/usr/share/fonts/TTF/OpenSans-ExtraBold.ttf", + 20.0, + (0, 0, 0, 128), + )), + (0, 0), + (64, 64), + ), + Element::new( + cowgen::Media::Image(include_bytes!("mariah.jpg").to_vec()), + (200, 400), + (800, 200), + ), + ], )? - .produce(); - let mut f = File::create("out.png").unwrap(); + .produce()?; + let mut f = File::create("out2.png").unwrap(); f.write_all(&out).unwrap(); f.flush().unwrap(); Ok(())