colr-types 0.3.1

Color model ZSTs and marker traits for colr.
Documentation
//! Signal encoding from linear light to a stored signal value.

use crate::math::Float;
use crate::transfer::*;

/// Encode a value from linear light to a stored signal (OETF).
///
/// Implement for scalar `F: Float` to get a transfer function at any precision.
/// A blanket impl provides `Encode<[F; N]>` automatically for any `Encode<F>`.
pub trait Encode<T>: TransferFunction {
    /// Convert a linear-light value to an encoded signal value.
    fn encode(v: T) -> T;
}

impl<F: Float, TF: Encode<F>, const N: usize> Encode<[F; N]> for TF {
    #[inline(always)]
    fn encode(v: [F; N]) -> [F; N] {
        v.map(|x| TF::encode(x))
    }
}

impl<F: Float> Encode<F> for Linear {
    #[inline(always)]
    fn encode(v: F) -> F {
        v
    }
}

impl<F: Float> Encode<F> for Srgb {
    #[inline(always)]
    fn encode(x: F) -> F {
        let zero = F::ZERO;
        let one = F::ONE;
        let neg_one = zero - one;
        let sign = if x < zero { neg_one } else { one };
        let abs = x.abs();
        let threshold = F::from_f64(Self::LINEAR_THRESHOLD as f64);
        let slope = F::from_f64(Self::LINEAR_SLOPE as f64);
        let alpha = F::from_f64(Self::ALPHA as f64);
        let inv_gamma = F::from_f64(1.0 / Self::GAMMA as f64);
        sign * if abs <= threshold {
            abs * slope
        } else {
            alpha * abs.powf(inv_gamma) - (alpha - one)
        }
    }
}

impl<F: Float> Encode<F> for Rec709 {
    #[inline(always)]
    fn encode(x: F) -> F {
        let zero = F::ZERO;
        let one = F::ONE;
        let neg_one = zero - one;
        let sign = if x < zero { neg_one } else { one };
        let abs = x.abs();
        let threshold = F::from_f64(Self::LINEAR_THRESHOLD as f64);
        let slope = F::from_f64(Self::LINEAR_SLOPE as f64);
        let alpha = F::from_f64(Self::ALPHA as f64);
        let power = F::from_f64(Self::POWER as f64);
        sign * if abs <= threshold {
            abs * slope
        } else {
            alpha * abs.powf(power) - (alpha - one)
        }
    }
}

impl<F: Float> Encode<F> for Pq {
    #[inline(always)]
    fn encode(x: F) -> F {
        let zero = F::ZERO;
        let m1 = F::from_f64(Self::M1 as f64);
        let m2 = F::from_f64(Self::M2 as f64);
        let c1 = F::from_f64(Self::C1 as f64);
        let c2 = F::from_f64(Self::C2 as f64);
        let c3 = F::from_f64(Self::C3 as f64);
        let one = F::ONE;
        let y_m1 = x.max(zero).powf(m1);
        ((c1 + c2 * y_m1) / (one + c3 * y_m1)).powf(m2)
    }
}

impl<F: Float> Encode<F> for Hlg {
    #[inline(always)]
    fn encode(x: F) -> F {
        let zero = F::ZERO;
        let one = F::ONE;
        let neg_one = zero - one;
        let abs = x.abs();
        let sign = if x < zero { neg_one } else { one };
        let threshold = F::from_f64(Self::LINEAR_THRESHOLD as f64);
        let a = F::from_f64(Self::A as f64);
        let b = F::from_f64(Self::B as f64);
        let c = F::from_f64(Self::C as f64);
        let three = F::from_f64(3.0);
        let twelve = F::from_f64(12.0);
        if abs <= threshold {
            sign * (three * abs).sqrt()
        } else {
            sign * (a * (twelve * abs - b).max(F::MIN_POSITIVE).ln() + c)
        }
    }
}

impl<F: Float> Encode<F> for ProPhoto {
    #[inline(always)]
    fn encode(x: F) -> F {
        let zero = F::ZERO;
        let one = F::ONE;
        let neg_one = zero - one;
        let sign = if x < zero { neg_one } else { one };
        let abs = x.abs();
        let threshold = F::from_f64(Self::LINEAR_THRESHOLD as f64);
        let slope = F::from_f64(Self::LINEAR_SLOPE as f64);
        let inv_gamma = F::from_f64(1.0 / Self::GAMMA as f64);
        sign * if abs <= threshold {
            abs * slope
        } else {
            abs.powf(inv_gamma)
        }
    }
}

impl<F: Float> Encode<F> for AcesCc {
    #[inline(always)]
    fn encode(x: F) -> F {
        let zero = F::ZERO;
        let scale = F::from_f64(Self::LOG_SCALE as f64);
        let offset = F::from_f64(Self::LOG_OFFSET as f64);
        let log2_recip = F::from_f64(Self::LOG2_RECIP as f64);
        let neg16: F = F::from_f64(2.0f64.powi(-16));
        let neg15: F = F::from_f64(2.0f64.powi(-15));
        let half = F::from_f64(0.5);
        if x < zero {
            (neg16.ln() * log2_recip + offset) / scale
        } else if x < neg15 {
            (neg16 + x * half).ln() * log2_recip * (F::ONE / scale) + offset / scale
        } else {
            x.ln() * log2_recip * (F::ONE / scale) + offset / scale
        }
    }
}

impl<F: Float> Encode<F> for AcesCct {
    #[inline(always)]
    fn encode(x: F) -> F {
        let x_brk = F::from_f64(Self::X_BRK as f64);
        let a = F::from_f64(Self::A as f64);
        let b = F::from_f64(Self::B as f64);
        let scale = F::from_f64(Self::LOG_SCALE as f64);
        let offset = F::from_f64(Self::LOG_OFFSET as f64);
        let log2_recip = F::from_f64(Self::LOG2_RECIP as f64);
        if x <= x_brk {
            a * x + b
        } else {
            (x.ln() * log2_recip + offset) / scale
        }
    }
}

impl<F: Float> Encode<F> for DciP3 {
    #[inline(always)]
    fn encode(x: F) -> F {
        let inv_gamma = F::from_f64(1.0 / Self::GAMMA as f64);
        x.max(F::ZERO).powf(inv_gamma)
    }
}