PronounsToday/web/src/ogp_images.rs

86 lines
2.5 KiB
Rust

#![cfg(feature = "ogp_images")]
use crate::contrast::generate_color_pair_rgba;
use image::{DynamicImage, ImageBuffer, Pixel, Rgb, Rgba};
use lazy_static::lazy_static;
use pronouns_today::{Pronoun, util::{self, pcg64_seed, seed_with_today}};
use rusttype::{Font, Scale, point};
const SCALE: u32 = 400;
const FONT_SCALE: f32 = 250.0;
const FONT_DATA: &[u8] = include_bytes!("../assets/LeagueGothic.otf");
lazy_static! {
static ref FONT: Font<'static> = Font::try_from_bytes(FONT_DATA)
.expect("Invalid font data in build. This build and binary is invalid, and cannot be used.");
}
pub fn render_today(pronouns: &Pronoun, name: &str) -> ImageBuffer<Rgb<u8>, Vec<u8>> {
let mut state = util::INITIAL_STATE;
seed_with_today(&mut state);
pcg64_seed(&mut state, name.as_bytes());
render(pronouns, &mut state)
}
pub fn render(pronouns: &Pronoun, pcg64: &mut u128) -> ImageBuffer<Rgb<u8>, Vec<u8>> {
let (color1, color2) = generate_color_pair_rgba(pcg64);
render_with_colors(pronouns, color1, color2)
}
pub fn render_with_colors(pronouns: &Pronoun, color1: Rgba<u8>, color2: Rgba<u8>) -> ImageBuffer<Rgb<u8>, Vec<u8>> {
let scale = Scale::uniform(FONT_SCALE);
let mut output_image = ImageBuffer::from_fn(SCALE * 2, SCALE, |x, _| {
if x < SCALE {
color1
} else {
color2
}
});
for (color, text, offset) in [(color2, &pronouns.subject_pronoun, 0), (color1, &pronouns.object_pronoun, 400)] {
let v_metrics = FONT.v_metrics(scale);
let glyphs: Vec<_> = FONT
.layout(&text.to_uppercase(), scale, point(0.0, v_metrics.ascent))
.collect();
let min_x = glyphs
.first()
.map(|g| g.pixel_bounding_box().unwrap().min.x)
.unwrap_or(0);
let max_x = glyphs
.last()
.map(|g| g.pixel_bounding_box().unwrap().max.x)
.unwrap_or(0);
let width = max_x - min_x;
let height = v_metrics.ascent - v_metrics.descent;
// Recompute offset to center text on page
let x_offset = (SCALE as u32 - width as u32) / 2 + offset;
let y_offset = ((SCALE as f32 - height) / 2.0) as u32;
// Draw glyphs onto the buffer
for glyph in glyphs {
if let Some(bounding_box) = glyph.pixel_bounding_box() {
glyph.draw(|x, y, v| {
if v > 0.0 {
let mut overlay_color = color;
let alpha = &mut overlay_color.channels_mut()[3];
*alpha = (*alpha as f32 * v) as u8;
output_image.get_pixel_mut(
x + x_offset + bounding_box.min.x as u32,
y + y_offset + bounding_box.min.y as u32
).blend(&overlay_color);
}
});
}
}
}
DynamicImage::ImageRgba8(output_image).into_rgb8()
}