colr 0.3.1

A general purpose, extensible color type unifying color models and their operations at the type level.
Documentation
//! Channel-wise addition of two colors in the same color space.

use core::ops::{Add, AddAssign};

use crate::Color;
use crate::BackingStore;
use crate::ChannelMap;
use crate::illuminant::Illuminant;
use crate::model::{Lab, Oklab, Rgb, Spectral, SpectralKind, WavelengthGrid};
use crate::primaries::Primaries;
use crate::transfer::Linear;

macro_rules! impl_add_perceptual {
    ($name:ident, $bound:tt) => {
        impl<$bound> Add for Color<[f32; 3], $name<$bound>> where $bound: Illuminant {
            type Output = Self;
            #[inline(always)]
            fn add(self, rhs: Self) -> Self {
                let [a0, a1, a2] = self.inner();
                let [b0, b1, b2] = rhs.inner();
                Color::new([a0 + b0, a1 + b1, a2 + b2])
            }
        }
        impl<$bound, const O: usize> Add for Color<[f32; 4], $name<$bound, O>> where $bound: Illuminant {
            type Output = Self;
            #[inline(always)]
            fn add(self, rhs: Self) -> Self {
                let s = self.inner();
                let r = rhs.inner();
                let mut out = s;
                out[O] = s[O] + r[O];
                out[O + 1] = s[O + 1] + r[O + 1];
                out[O + 2] = s[O + 2] + r[O + 2];
                Color::new(out)
            }
        }
    };
    ($name:ident) => {
        impl Add for Color<[f32; 3], $name> {
            type Output = Self;
            #[inline(always)]
            fn add(self, rhs: Self) -> Self {
                let [a0, a1, a2] = self.inner();
                let [b0, b1, b2] = rhs.inner();
                Color::new([a0 + b0, a1 + b1, a2 + b2])
            }
        }
        impl<const O: usize> Add for Color<[f32; 4], $name<O>> {
            type Output = Self;
            #[inline(always)]
            fn add(self, rhs: Self) -> Self {
                let s = self.inner();
                let r = rhs.inner();
                let mut out = s;
                out[O] = s[O] + r[O];
                out[O + 1] = s[O + 1] + r[O + 1];
                out[O + 2] = s[O + 2] + r[O + 2];
                Color::new(out)
            }
        }
    };
}

impl_add_perceptual!(Lab, W);
impl_add_perceptual!(Oklab);

impl<const BANDS: usize, G, K> Add for Color<[f32; BANDS], Spectral<BANDS, G, K>>
where
    G: WavelengthGrid<BANDS>,
    K: SpectralKind,
    Spectral<BANDS, G, K>: BackingStore<[f32; BANDS]>,
{
    type Output = Self;
    #[inline(always)]
    fn add(self, rhs: Self) -> Self {
        let a = self.inner();
        let b = rhs.inner();
        let mut out = a;
        for i in 0..BANDS { out[i] = a[i] + b[i]; }
        Color::new(out)
    }
}

impl<P, L> Add for Color<[f32; 3], Rgb<P, Linear, L>>
where
    P: Primaries,
    L: BackingStore<[f32; 3]>,
{
    type Output = Self;
    #[inline(always)]
    fn add(self, rhs: Self) -> Self {
        let [a0, a1, a2] = self.inner();
        let [b0, b1, b2] = rhs.inner();
        Color::new([a0 + b0, a1 + b1, a2 + b2])
    }
}

impl<P, L> Add for Color<[f32; 4], Rgb<P, Linear, L>>
where
    P: Primaries,
    L: BackingStore<[f32; 4]> + ChannelMap<4>,
{
    type Output = Self;

    /// Adds color channels. Alpha passes through from `self`.
    #[inline(always)]
    fn add(self, rhs: Self) -> Self {
        let s = self.inner();
        let r = rhs.inner();
        let [ri, gi, bi, _] = L::INDICES;
        let mut out = s;
        out[ri] = s[ri] + r[ri];
        out[gi] = s[gi] + r[gi];
        out[bi] = s[bi] + r[bi];
        Color::new(out)
    }
}

impl<const BANDS: usize, G, K> AddAssign for Color<[f32; BANDS], Spectral<BANDS, G, K>>
where
    G: WavelengthGrid<BANDS>,
    K: SpectralKind,
    Spectral<BANDS, G, K>: BackingStore<[f32; BANDS]>,
{
    #[inline(always)]
    fn add_assign(&mut self, rhs: Self) {
        let b = rhs.inner();
        let mut out = self.inner();
        for i in 0..BANDS { out[i] += b[i]; }
        *self = Color::new(out);
    }
}

impl<P, L> AddAssign for Color<[f32; 3], Rgb<P, Linear, L>>
where
    P: Primaries,
    L: BackingStore<[f32; 3]>,
{
    #[inline(always)]
    fn add_assign(&mut self, rhs: Self) {
        let [a0, a1, a2] = self.inner();
        let [b0, b1, b2] = rhs.inner();
        *self = Color::new([a0 + b0, a1 + b1, a2 + b2]);
    }
}