#![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, Rgba) { let (color1, color2) = generate_color_pair(state); let (color1, color2): (Rgb, Rgb) = ( color1.into(), color2.into(), ); (color1.to_rgba(), color2.to_rgba()) } pub fn relative_lum(color: &[u8; 3]) -> f32 { let color_mapped: Vec = 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 }