coloremetry 0.2.0

small color library written in Rust
Documentation
/*
 *    Copyright 2025 Jared Davis
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
use crate::cie::illumination::Illumination;
use crate::cie::lab::Lab;
use crate::cie::rgb::RGB;

#[macro_export]
macro_rules! illumination {
    ($xyz:expr, $rgb:expr, $reference:expr) => {
        Illumination {
            x: $xyz[0],
            y: $xyz[1],
            z: $xyz[2],
            r: $rgb[0],
            g: $rgb[1],
            b: $rgb[2],
            reference: XYZ {
                x: $reference[0],
                y: $reference[1],
                z: $reference[2],
            },
        }
    };
}

#[derive(Clone, Copy)]
pub struct XYZ {
    pub x: f32,
    pub y: f32,
    pub z: f32,
}

impl XYZ {
    pub fn lab(self, illumination: Illumination) -> Lab {
        let fx = forward(self.x * illumination.reference.x);
        let fy = forward(self.y * illumination.reference.y);
        let fz = forward(self.z * illumination.reference.z);

        let l = (116.0 * fy) - 16.0;
        let a = 500.0 * (fx - fy);
        let b = 200.0 * (fy - fz);

        Lab { l, a, b }
    }

    pub fn rgb(self, illumination: Illumination) -> RGB {
        let r =
            illumination.r[0] * self.x + illumination.r[1] * self.y + illumination.r[2] * self.z;
        let g =
            illumination.g[0] * self.x + illumination.g[1] * self.y + illumination.g[2] * self.z;
        let b =
            illumination.b[0] * self.x + illumination.b[1] * self.y + illumination.b[2] * self.z;

        RGB::new(
            (gamma(r) * 255.0).max(0.0).min(255.0) as u8,
            (gamma(g) * 255.0).max(0.0).min(255.0) as u8,
            (gamma(b) * 255.0).max(0.0).min(255.0) as u8,
        )
    }
}

fn gamma(v: f32) -> f32 {
    if v < 0.0031308 {
        v * 12.92
    } else {
        1.055 * v.powf(1.0 / 2.4) - 0.055
    }
}

fn forward(v: f32) -> f32 {
    if v > 0.008856451679035631 {
        v.powf(1.0 / 3.0)
    } else {
        v * 7.787037037037037 + 0.13793103448275862
    }
}