2021-10-25 15:43:52 +00:00
|
|
|
#![cfg(feature = "ogp_images")]
|
|
|
|
|
2021-10-25 17:07:14 +00:00
|
|
|
use crate::contrast::generate_color_pair_rgba;
|
|
|
|
|
2021-10-25 15:43:52 +00:00
|
|
|
use image::{DynamicImage, ImageBuffer, Pixel, Rgb, Rgba};
|
|
|
|
use lazy_static::lazy_static;
|
2021-10-25 17:07:14 +00:00
|
|
|
use pronouns_today::{Pronoun, util::{self, pcg64_seed, seed_with_today}};
|
2021-10-25 15:43:52 +00:00
|
|
|
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.");
|
|
|
|
}
|
|
|
|
|
2021-10-25 17:07:14 +00:00
|
|
|
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);
|
2021-10-25 15:43:52 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-25 17:07:52 +00:00
|
|
|
DynamicImage::ImageRgba8(output_image).into_rgb8()
|
2021-10-25 15:43:52 +00:00
|
|
|
}
|