Finished PNG handling w/ magick convertion to png32
This commit is contained in:
parent
18938dd2a0
commit
747443d2b8
|
@ -8,3 +8,4 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ril = { version = "0", features = ["all"] }
|
ril = { version = "0", features = ["all"] }
|
||||||
|
magick_rust = { version = "0.16" }
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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![
|
||||||
cowgen::Media::Text(Text::new(
|
Element::new(
|
||||||
"Hello",
|
cowgen::Media::Text(Text::new(
|
||||||
"/usr/share/fonts/TTF/OpenSans-ExtraBold.ttf",
|
"Hello",
|
||||||
12.0,
|
"/usr/share/fonts/TTF/OpenSans-ExtraBold.ttf",
|
||||||
(0, 0, 0),
|
20.0,
|
||||||
)),
|
(0, 0, 0, 128),
|
||||||
(0, 0),
|
)),
|
||||||
(64, 64),
|
(0, 0),
|
||||||
)],
|
(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(())
|
||||||
|
|
Loading…
Reference in New Issue