colr 0.3.0

A general purpose, extensible color type unifying color models and their operations at the type level.
//! Alpha compositing operations for premultiplied linear colors.

use crate::Color;
use crate::layout::{Abgr, Argb, Bgra, Rgba};
use crate::model::{LumaAlpha, Rgb};
use crate::primaries::Primaries;
use crate::transfer::Linear;
use crate::{Premultiplied, Straight};

macro_rules! impl_blend {
    ($layout:ident) => {
        impl<P> Color<[f32; 4], Rgb<P, Linear, $layout<Straight>>>
        where
            P: Primaries,
        {
            /// Multiply the color channels by alpha, converting to premultiplied form.
            #[inline(always)]
            pub fn premultiply(self) -> Color<[f32; 4], Rgb<P, Linear, $layout<Premultiplied>>> {
                let [r, g, b, a] = self.inner();
                Color::new([r * a, g * a, b * a, a])
            }
        }

        impl<P> Color<[f32; 4], Rgb<P, Linear, $layout<Premultiplied>>>
        where
            P: Primaries,
        {
            /// Divide the color channels by alpha, converting to straight form.
            ///
            /// Fully transparent pixels (alpha = 0) return a zero color.
            #[inline(always)]
            pub fn unpremultiply(self) -> Color<[f32; 4], Rgb<P, Linear, $layout<Straight>>> {
                let [r, g, b, a] = self.inner();
                if a == 0.0 {
                    Color::new([0.0, 0.0, 0.0, 0.0])
                } else {
                    Color::new([r / a, g / a, b / a, a])
                }
            }

            /// Porter-Duff over: composite `self` over `dst`. Both colors must be premultiplied.
            #[inline(always)]
            pub fn over(self, dst: Self) -> Self {
                let [sr, sg, sb, sa] = self.inner();
                let [dr, dg, db, da] = dst.inner();
                let inv = 1.0 - sa;
                Color::new([sr + dr * inv, sg + dg * inv, sb + db * inv, sa + da * inv])
            }
        }
    };
}

impl_blend!(Rgba);
impl_blend!(Bgra);
impl_blend!(Argb);
impl_blend!(Abgr);

// Luma+alpha blend ops.

impl<P> Color<[f32; 2], LumaAlpha<P, Linear, Straight>>
where
    P: Primaries,
{
    /// Multiply luma by alpha, converting to premultiplied form.
    #[inline(always)]
    pub fn premultiply(self) -> Color<[f32; 2], LumaAlpha<P, Linear, Premultiplied>> {
        let [y, a] = self.inner();
        Color::new([y * a, a])
    }
}

impl<P> Color<[f32; 2], LumaAlpha<P, Linear, Premultiplied>>
where
    P: Primaries,
{
    /// Divide luma by alpha, converting to straight form.
    ///
    /// Fully transparent pixels (alpha = 0) return a zero color.
    #[inline(always)]
    pub fn unpremultiply(self) -> Color<[f32; 2], LumaAlpha<P, Linear, Straight>> {
        let [y, a] = self.inner();
        if a == 0.0 {
            Color::new([0.0, 0.0])
        } else {
            Color::new([y / a, a])
        }
    }

    /// Porter-Duff over: composite `self` over `dst`. Both must be premultiplied.
    #[inline(always)]
    pub fn over(self, dst: Self) -> Self {
        let [sy, sa] = self.inner();
        let [dy, da] = dst.inner();
        let inv = 1.0 - sa;
        Color::new([sy + dy * inv, sa + da * inv])
    }
}