#![feature(random, string_remove_matches)] use ab_glyph::{FontRef, PxScale}; use anyhow::Result; use image::{DynamicImage, ImageBuffer, ImageEncoder, Rgba, codecs::qoi::QoiEncoder}; use imageproc::drawing::{draw_text_mut, text_size}; use std::{ hash::{Hash, Hasher}, io::Write, }; pub fn generate_fallback(name: &str, output: &mut dyn Write) -> Result<()> { let width = 1024; let height = (width * 1000) / 707; let mut image = ImageBuffer::, Vec>::new(width, height); let text = name; let bg = random_accent(text, 0.4); image.pixels_mut().for_each(|p| *p = bg); let font = FontRef::try_from_slice(include_bytes!( "/usr/share/fonts/cantarell/Cantarell-VF.otf" )) .unwrap(); let font_bold = FontRef::try_from_slice(include_bytes!( "/usr/share/fonts/TTF/OpenSans-CondensedExtraBold.ttf" )) .unwrap(); let mut bgtext = text.to_string(); bgtext.remove_matches(&[',', ' ']); let bgtext = bgtext.repeat(3); let scale = PxScale { x: 1000., y: 1000. }; let (w, h) = text_size(scale, &font, &bgtext); for i in -1..4 { draw_text_mut( &mut image, random_accent(&text, 0.6), width as i32 / 2 - w as i32 / 2 + i * h as i32, i * h as i32 * 2 / 3, scale, &font_bold, &bgtext, ); } image.enumerate_pixels_mut().for_each(|(_x, y, p)| { let f = 1. - (y as f32 / height as f32) * 0.5; p.0[0] *= f; p.0[1] *= f; p.0[2] *= f; }); let scale = PxScale { x: 200., y: 200. }; let (w, _h) = text_size(scale, &font, text); draw_text_mut( &mut image, Rgba([1., 1., 1., 1.]), width as i32 / 2 - w as i32 / 2, height as i32 * 3 / 4, scale, &font, text, ); let image = DynamicImage::from(image).to_rgb8(); QoiEncoder::new(output).write_image( image.as_raw(), image.width(), image.height(), image::ExtendedColorType::Rgb8, )?; Ok(()) } struct XorshiftHasher(u64); impl Hasher for XorshiftHasher { fn finish(&self) -> u64 { self.0 } fn write(&mut self, bytes: &[u8]) { for b in bytes { self.0 = self .0 .wrapping_add((*b as u64).wrapping_shl(24) + (*b as u64).wrapping_shl(56)); self.0 ^= self.0.wrapping_shl(13); self.0 ^= self.0.wrapping_shr(7); self.0 ^= self.0.wrapping_shl(17); } } } fn random_accent(text: &str, y: f32) -> Rgba { let mut hasher = XorshiftHasher(0); text.hash(&mut hasher); let h = hasher.finish(); let mut u = (h >> 32) as u32 as f32 / u32::MAX as f32; let mut v = (h) as u32 as f32 / u32::MAX as f32; u *= 0.2; v *= 0.2; Rgba([y - u * 0.5 - v * 0.5, y + v, y + u, 1.]) }