Compare commits
	
		
			No commits in common. "e7e5236b80d21d768e4246f0c0e27b1f54f00345" and "1442d3821a31fa86719125b13d17c9a4afbdb41e" have entirely different histories.
		
	
	
		
			e7e5236b80
			...
			1442d3821a
		
	
		|  | @ -1,5 +1,3 @@ | |||
| # cowmic | ||||
| 
 | ||||
| generates memes | ||||
| 
 | ||||
| `mogrify -define png:format=png32 -format png *.png` | ||||
|  |  | |||
|  | @ -7,5 +7,3 @@ edition = "2021" | |||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||
| 
 | ||||
| [dependencies] | ||||
| ril = { version = "0", features = ["all"] } | ||||
| magick_rust = { version = "0.16" } | ||||
|  |  | |||
|  | @ -1,146 +1,3 @@ | |||
| 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<T: AsRef<str>, V: AsRef<Path>> { | ||||
|     elements: Vec<Element<T, V>>, | ||||
|     base: Image<Rgba>, | ||||
| } | ||||
| 
 | ||||
| impl<T: AsRef<str>, V: AsRef<Path>> Template<T, V> { | ||||
|     pub fn new(base: Vec<u8>, elements: Vec<Element<T, V>>) -> Result<Self, CowError> { | ||||
|         Ok(Self { | ||||
|             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, | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     pub fn produce(self) -> Result<Vec<u8>, 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::<u8>::new(); | ||||
|         base_image.encode(ImageFormat::Png, &mut buf).unwrap(); | ||||
| 
 | ||||
|         Ok(buf) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct Element<T: AsRef<str>, V: AsRef<Path>> { | ||||
|     position: (i32, i32), | ||||
|     dimensions: (u32, u32), | ||||
|     media: Media<T, V>, | ||||
| } | ||||
| 
 | ||||
| impl<T: AsRef<str>, V: AsRef<Path>> Element<T, V> { | ||||
|     pub fn new(media: Media<T, V>, position: (i32, i32), dimensions: (u32, u32)) -> Self { | ||||
|         Self { | ||||
|             position, | ||||
|             dimensions, | ||||
|             media, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn into_image(self) -> Result<Image, CowError> { | ||||
|         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::from_reader(File::open(text.font).unwrap(), 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<T: AsRef<str>, V: AsRef<Path>> { | ||||
|     Text(Text<T, V>), | ||||
|     Image(Vec<u8>), | ||||
| } | ||||
| 
 | ||||
| pub struct Text<T: AsRef<str>, V: AsRef<Path>> { | ||||
|     text: T, | ||||
|     font: V, | ||||
|     size: f32, | ||||
|     fill: (u8, u8, u8, u8), | ||||
| } | ||||
| 
 | ||||
| impl<T: AsRef<str>, V: AsRef<Path>> Text<T, V> { | ||||
|     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<ril::Error> for CowError { | ||||
|     fn from(r: ril::Error) -> Self { | ||||
|         CowError::Other(r.to_string()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<MagickError> for CowError { | ||||
|     fn from(m: MagickError) -> Self { | ||||
|         CowError::Magick(m.0.to_owned()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     #[test] | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 51 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 132 KiB | 
|  | @ -1,31 +1,3 @@ | |||
| use std::{fs::File, io::Write}; | ||||
| 
 | ||||
| use cowgen::{CowError, Element, Text}; | ||||
| 
 | ||||
| fn main() -> Result<(), CowError> { | ||||
|     let out = cowgen::Template::new( | ||||
|         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("out2.png").unwrap(); | ||||
|     f.write_all(&out).unwrap(); | ||||
|     f.flush().unwrap(); | ||||
|     Ok(()) | ||||
| fn main() { | ||||
|     println!("Hello, world!"); | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue