use std::fs::File;
use std::io::BufWriter;
use rand::Rng;
use colr::illuminant::D65;
use colr::layout::Rgb;
use colr::{Color, LinearSrgb, Oklch, Srgb, Xyz};
fn srgb_to_oklch(c: Color<[f32; 3], Srgb<Rgb>>) -> Color<[f32; 3], Oklch> {
let linear: Color<[f32; 3], LinearSrgb<Rgb>> = c.decode();
let xyz: Color<[f32; 3], Xyz<D65>> = linear.into();
xyz.into()
}
fn oklch_to_srgb(c: Color<[f32; 3], Oklch>) -> Color<[f32; 3], Srgb<Rgb>> {
let xyz: Color<[f32; 3], Xyz<D65>> = c.into();
let linear: Color<[f32; 3], LinearSrgb<Rgb>> = xyz.into();
linear.encode()
}
fn is_displayable(c: Color<[f32; 3], Srgb<Rgb>>) -> bool {
c.inner().iter().all(|&v| (0.0..=1.0).contains(&v))
}
const STEPS: usize = 8;
fn main() {
let red_srgb: Color<[f32; 3], Srgb<Rgb>> = Color::new([0.769, 0.180, 0.154]);
let blue_srgb: Color<[f32; 3], Srgb<Rgb>> = Color::new([0.114, 0.240, 0.845]);
let red = srgb_to_oklch(red_srgb);
let blue = srgb_to_oklch(blue_srgb);
let all_displayable = (0..=STEPS)
.map(|i| red.lerp(blue, i as f32 / STEPS as f32))
.map(oklch_to_srgb)
.all(is_displayable);
assert!(
all_displayable,
"some gradient steps cannot be shown on a standard screen, use less vivid colours"
);
let width: u32 = 512;
let height: u32 = 64;
let columns: Vec<Color<[f32; 3], Srgb<Rgb>>> = (0..width)
.map(|x| {
let t = x as f32 / (width - 1) as f32;
oklch_to_srgb(red.lerp(blue, t))
})
.collect();
save_to_png("oklch_gradient.png", &columns, width, height);
println!("Wrote oklch_gradient.png");
}
fn save_to_png(path: &str, columns: &[Color<[f32; 3], Srgb<Rgb>>], width: u32, height: u32) {
let mut rng = rand::rng();
let range = 0.0..(1.0 / 255.0);
let mut data = vec![0u8; (width * height * 3) as usize];
for y in 0..height as usize {
for (x, &colour) in columns.iter().enumerate() {
let dither = rng.random_range(range.clone()) - rng.random_range(range.clone());
let [r, g, b] = colour.to_u8_dithered(dither).inner();
let dst = (y * width as usize + x) * 3;
data[dst] = r;
data[dst + 1] = g;
data[dst + 2] = b;
}
}
let file = BufWriter::new(File::create(path).unwrap());
let mut encoder = png::Encoder::new(file, width, height);
encoder.set_color(png::ColorType::Rgb);
encoder.set_depth(png::BitDepth::Eight);
encoder
.write_header()
.unwrap()
.write_image_data(&data)
.unwrap();
}