aboutsummaryrefslogtreecommitdiff
path: root/import/fallback_generator/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'import/fallback_generator/src/lib.rs')
-rw-r--r--import/fallback_generator/src/lib.rs103
1 files changed, 103 insertions, 0 deletions
diff --git a/import/fallback_generator/src/lib.rs b/import/fallback_generator/src/lib.rs
new file mode 100644
index 0000000..efb6579
--- /dev/null
+++ b/import/fallback_generator/src/lib.rs
@@ -0,0 +1,103 @@
+#![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::<Rgba<f32>, Vec<f32>>::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<f32> {
+ 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.])
+}