PronounsToday/web/src/contrast.rs

73 lines
1.8 KiB
Rust

#![cfg(feature = "ogp_images")]
use image::{Pixel, Rgb, Rgba};
use pronouns_today::util::pcg64;
pub fn generate_color_pair(state: &mut u128) -> ([u8; 3], [u8; 3]) {
let mut background = random_color(state);
let mut foreground;
loop {
foreground = random_color(state);
if meets_criteria(&foreground, &background) {
break;
}
background = random_color(state);
if meets_criteria(&foreground, &background) {
break;
}
}
(foreground, background)
}
pub fn generate_color_pair_rgba(state: &mut u128) -> (Rgba<u8>, Rgba<u8>) {
let (color1, color2) = generate_color_pair(state);
let (color1, color2): (Rgb<u8>, Rgb<u8>) = (
color1.into(),
color2.into(),
);
(color1.to_rgba(), color2.to_rgba())
}
pub fn relative_lum(color: &[u8; 3]) -> f32 {
let color_mapped: Vec<f32> = color.iter()
.map(|val| {
let val = *val as f32 / 255.0;
if val < 0.03928 {
val / 12.92
} else {
((val+0.055)/1.055).powf(2.4)
}
})
.collect();
return 0.2126 * color_mapped[0] + 0.7152 * color_mapped[1] + 0.0722 * color_mapped[2]
}
pub fn check_contrast(color1: &[u8; 3], color2: &[u8; 3]) -> f32 {
let a = 0.05 + relative_lum(color1);
let b = 0.05 + relative_lum(color2);
(a / b).max(b / a)
}
pub fn get_saturation(color: &[u8; 3]) -> f32 {
let chroma_max = *(color.iter().max().unwrap());
let chroma_min = *(color.iter().min().unwrap());
return ((chroma_max - chroma_min) as f32) / chroma_max as f32;
}
pub fn random_color(state: &mut u128) -> [u8; 3] {
let random = pcg64(state).to_le_bytes();
return [
random[0],
random[1],
random[2],
]
}
pub fn meets_criteria(foreground: &[u8; 3], background: &[u8; 3]) -> bool {
return check_contrast(foreground, background) >= 5.0 &&
get_saturation(background) < 0.65 &&
get_saturation(foreground) < 0.65
}