colr 0.3.0

A general purpose, extensible color type unifying color models and their operations at the type level.
//! Two practical color pipelines demonstrating the core API.
//!
//! The first blends two sRGB colors in linear light, which is required for
//! physically correct compositing. The second converts an ACEScg render result
//! to sRGB for display, adapting from the ACES white point to D65 along the way.

use colr::chromatic_adaptation::Bradford;
use colr::illuminant::{AcesWhitePoint, D65};
use colr::{AcesCg, Color, LinearSrgb, Srgb, Xyz};

fn main() {
    let a: Color<[f32; 4], Srgb> = Color::new([0.9, 0.2, 0.1, 1.0]);
    let b: Color<[f32; 4], Srgb> = Color::new([0.1, 0.4, 0.9, 1.0]);

    // Decode before blending. Mixing encoded sRGB values directly produces the
    // wrong color because the transfer function is nonlinear.
    let blended: Color<[f32; 4], LinearSrgb> = a.decode().lerp(b.decode(), 0.5);

    let composite: Color<[f32; 4], Srgb> = blended.encode();
    println!("composite: {:?}", composite.inner());

    // A render output in ACEScg. One channel is above the sRGB display range.
    let acescg: Color<[f32; 4], AcesCg> = Color::new([1.4, 0.9, 0.3, 1.0]);

    // Lift to XYZ under the ACES white point, then adapt to D65 for sRGB.
    let xyz_d65: Color<[f32; 4], Xyz<D65>> =
        Color::<_, Xyz<AcesWhitePoint>>::from(acescg).adapt::<D65, Bradford>();

    // Convert to linear sRGB. The first channel exceeds 1.0; clip for display
    // or pass the raw values to a tone mapper.
    let linear: Color<[f32; 4], LinearSrgb> = xyz_d65.into();
    let display: Color<[f32; 4], Srgb> = linear.clamp(0.0, 1.0).encode();
    println!("display:   {:?}", display.inner());
}