1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
use image::{imageops::overlay, load_from_memory, ImageError, RgbaImage}; use rand::random; use std::path::Path; mod emoji; fn sums_chunked(samples_a: &[u8], samples_b: &[u8]) -> (i32, i32, i32) { samples_a .chunks_exact(3) .zip(samples_b.chunks_exact(3)) .fold((0, 0, 0), |(r, g, b), (p_a, p_b)| { ( r + (p_a[0] as i32 - p_b[0] as i32), g + (p_a[1] as i32 - p_b[1] as i32), b + (p_a[2] as i32 - p_b[2] as i32), ) }) } pub fn generate_image( image_buffer: &[u8], iterations: u64, save_progress: bool, path: &Path, ) -> Result<Vec<u8>, ImageError> { let mut emoji_cache = emoji::EmojiCache::new(); let orig = load_from_memory(image_buffer)?.into_rgba(); let (width, height) = orig.dimensions(); let mut new_img = RgbaImage::new(width, height); let mut dist = sums_chunked(&orig, &new_img); for _ in 0..iterations { let e = emoji_cache.get_emoji(); let x: u32 = random::<u32>() % width; let y: u32 = random::<u32>() % height; let mut temp_img = new_img.clone(); overlay(&mut temp_img, e, x, y); let temp_dist = sums_chunked(&orig, &temp_img); if dist > temp_dist { new_img = temp_img; dist = temp_dist; if save_progress { new_img.save(path)?; } } } new_img.save(path)?; Ok(new_img.to_vec()) } #[cfg(test)] mod tests { #[test] fn it_works() { use crate::generate_image; use image::{jpeg::JPEGEncoder, open}; use std::path::Path; use std::time::Instant; let now = Instant::now(); let img = open(Path::new("./assets/georgia.jpg")).unwrap(); let mut output = Vec::new(); JPEGEncoder::new(&mut output).encode_image(&img).unwrap(); let path = Path::new("./g.png"); let new_img = generate_image(&output, 10000, true, path); println!("{}", now.elapsed().as_secs()); match new_img { Ok(_) => println!("OK"), Err(e) => println!("{}", e), } } }