Finished PNG handling w/ magick convertion to png32

This commit is contained in:
emilis 2022-11-02 23:24:18 +00:00
parent 18938dd2a0
commit 747443d2b8
3 changed files with 68 additions and 35 deletions

View File

@ -8,3 +8,4 @@ edition = "2021"
[dependencies] [dependencies]
ril = { version = "0", features = ["all"] } ril = { version = "0", features = ["all"] }
magick_rust = { version = "0.16" }

View File

@ -1,27 +1,43 @@
use magick_rust::{magick_wand_genesis, MagickError, MagickWand};
use ril::prelude::*; use ril::prelude::*;
use std::sync::Once;
use std::{fs::File, path::Path}; 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<T: AsRef<str>, V: AsRef<Path>> { pub struct Template<T: AsRef<str>, V: AsRef<Path>> {
elements: Vec<Element<T, V>>, elements: Vec<Element<T, V>>,
dimensions: (u32, u32), base: Image<Rgba>,
base: Image,
} }
impl<T: AsRef<str>, V: AsRef<Path>> Template<T, V> { impl<T: AsRef<str>, V: AsRef<Path>> Template<T, V> {
pub fn new(base: Vec<u8>, elements: Vec<Element<T, V>>) -> Result<Self, CowError> { pub fn new(base: Vec<u8>, elements: Vec<Element<T, V>>) -> Result<Self, CowError> {
let base: Image<Dynamic> = Image::from_bytes_inferred(base)?;
Ok(Self { Ok(Self {
dimensions: base.dimensions(), base: match Image::<Rgba>::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, elements,
base,
}) })
} }
pub fn produce(mut self) -> Vec<u8> { pub fn produce(self) -> Result<Vec<u8>, CowError> {
let size = self.base.dimensions(); let size = self.base.dimensions();
let mut base_image = self.base.with_overlay_mode(OverlayMode::Merge);
for element in self.elements { for element in self.elements {
let mut pos = element.position; let mut pos = element.position;
let mut img: Image = element.into(); let mut img: Image = element.into_image()?;
if pos.0 < 0 { if pos.0 < 0 {
img.crop(-pos.0 as u32, 0, size.0, size.1); img.crop(-pos.0 as u32, 0, size.0, size.1);
pos.0 = 0; pos.0 = 0;
@ -31,13 +47,12 @@ impl<T: AsRef<str>, V: AsRef<Path>> Template<T, V> {
pos.1 = 0; pos.1 = 0;
} }
self.base base_image.draw(&Paste::new(img.convert()).with_position(pos.0 as u32, pos.1 as u32));
.draw(&Paste::new(img).with_position(pos.0 as u32, pos.1 as u32));
} }
let mut buf = Vec::<u8>::new(); let mut buf = Vec::<u8>::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<T: AsRef<str>, V: AsRef<Path>> Element<T, V> {
media, media,
} }
} }
}
impl<T: AsRef<str>, V: AsRef<Path>> Into<Image> for Element<T, V> { fn into_image(self) -> Result<Image, CowError> {
fn into(self) -> Image {
match self.media { match self.media {
Media::Image(image) => image.resized( Media::Image(image) => Ok(Image::from_bytes_inferred(image)?.resized(
self.dimensions.0, self.dimensions.0,
self.dimensions.1, self.dimensions.1,
ResizeAlgorithm::Bicubic, ResizeAlgorithm::Bicubic,
), )),
Media::Text(text) => Image::new( Media::Text(text) => Ok(Image::new(
self.dimensions.0, self.dimensions.0,
self.dimensions.1, self.dimensions.1,
Dynamic::Rgba(Rgba::transparent()), Dynamic::Rgba(Rgba::transparent()),
@ -74,28 +87,33 @@ impl<T: AsRef<str>, V: AsRef<Path>> Into<Image> for Element<T, V> {
&TextSegment::new( &TextSegment::new(
&Font::from_reader(File::open(text.font).unwrap(), 12.0).unwrap(), &Font::from_reader(File::open(text.font).unwrap(), 12.0).unwrap(),
text.text, 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), .with_size(text.size),
), )),
} }
} }
} }
pub enum Media<T: AsRef<str>, V: AsRef<Path>> { pub enum Media<T: AsRef<str>, V: AsRef<Path>> {
Text(Text<T, V>), Text(Text<T, V>),
Image(Image), Image(Vec<u8>),
} }
pub struct Text<T: AsRef<str>, V: AsRef<Path>> { pub struct Text<T: AsRef<str>, V: AsRef<Path>> {
text: T, text: T,
font: V, font: V,
size: f32, size: f32,
fill: (u8, u8, u8), fill: (u8, u8, u8, u8),
} }
impl<T: AsRef<str>, V: AsRef<Path>> Text<T, V> { impl<T: AsRef<str>, V: AsRef<Path>> Text<T, V> {
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 { Self {
text, text,
font, font,
@ -107,6 +125,7 @@ impl<T: AsRef<str>, V: AsRef<Path>> Text<T, V> {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum CowError { pub enum CowError {
Magick(String),
Other(String), Other(String),
} }
@ -116,6 +135,12 @@ impl From<ril::Error> for CowError {
} }
} }
impl From<MagickError> for CowError {
fn from(m: MagickError) -> Self {
CowError::Magick(m.0.to_owned())
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#[test] #[test]

View File

@ -4,20 +4,27 @@ use cowgen::{CowError, Element, Text};
fn main() -> Result<(), CowError> { fn main() -> Result<(), CowError> {
let out = cowgen::Template::new( let out = cowgen::Template::new(
include_bytes!("cow2.png").to_vec(), include_bytes!("cow.png").to_vec(),
vec![Element::new( vec![
Element::new(
cowgen::Media::Text(Text::new( cowgen::Media::Text(Text::new(
"Hello", "Hello",
"/usr/share/fonts/TTF/OpenSans-ExtraBold.ttf", "/usr/share/fonts/TTF/OpenSans-ExtraBold.ttf",
12.0, 20.0,
(0, 0, 0), (0, 0, 0, 128),
)), )),
(0, 0), (0, 0),
(64, 64), (64, 64),
)], ),
Element::new(
cowgen::Media::Image(include_bytes!("mariah.jpg").to_vec()),
(200, 400),
(800, 200),
),
],
)? )?
.produce(); .produce()?;
let mut f = File::create("out.png").unwrap(); let mut f = File::create("out2.png").unwrap();
f.write_all(&out).unwrap(); f.write_all(&out).unwrap();
f.flush().unwrap(); f.flush().unwrap();
Ok(()) Ok(())