use crate::{
cast::ArrayCast,
clamp,
num::{Arithmetics, Clamp, One, Real, Zero},
stimulus::Stimulus,
Alpha,
};
use super::{blend_alpha, zip_colors, PreAlpha, Premultiply};
pub trait Compose {
#[must_use]
fn over(self, other: Self) -> Self;
#[must_use]
fn inside(self, other: Self) -> Self;
#[must_use]
fn outside(self, other: Self) -> Self;
#[must_use]
fn atop(self, other: Self) -> Self;
#[must_use]
fn xor(self, other: Self) -> Self;
#[must_use]
fn plus(self, other: Self) -> Self;
}
impl<C, T, const N: usize> Compose for PreAlpha<C>
where
C: ArrayCast<Array = [T; N]> + Premultiply<Scalar = T>,
T: Real + Zero + One + Arithmetics + Clamp + Clone,
{
#[inline]
fn over(self, mut other: Self) -> Self {
for (src, dst) in zip_colors(self.color, &mut other.color) {
*dst = src + (T::one() - &self.alpha) * &*dst;
}
other.alpha = blend_alpha(self.alpha, other.alpha);
other
}
#[inline]
fn inside(self, mut other: Self) -> Self {
for (src, dst) in zip_colors(self.color, &mut other.color) {
*dst = src * &other.alpha;
}
other.alpha = clamp(self.alpha * other.alpha, T::zero(), T::one());
other
}
#[inline]
fn outside(self, mut other: Self) -> Self {
for (src, dst) in zip_colors(self.color, &mut other.color) {
*dst = src * (T::one() - &other.alpha);
}
other.alpha = clamp(self.alpha * (T::one() - other.alpha), T::zero(), T::one());
other
}
#[inline]
fn atop(self, mut other: Self) -> Self {
for (src, dst) in zip_colors(self.color, &mut other.color) {
*dst = src * &other.alpha + (T::one() - &self.alpha) * &*dst;
}
other.alpha = clamp(other.alpha, T::zero(), T::one());
other
}
#[inline]
fn xor(self, mut other: Self) -> Self {
let two = || T::one() + T::one();
for (src, dst) in zip_colors(self.color, &mut other.color) {
*dst = src * (T::one() - &other.alpha) + (T::one() - &self.alpha) * &*dst;
}
other.alpha = clamp(
self.alpha.clone() + &other.alpha - two() * self.alpha * other.alpha,
T::zero(),
T::one(),
);
other
}
#[inline]
fn plus(self, mut other: Self) -> Self {
for (src, dst) in zip_colors(self.color, &mut other.color) {
*dst = src + &*dst;
}
other.alpha = clamp(self.alpha + other.alpha, T::zero(), T::one());
other
}
}
impl<C> Compose for Alpha<C, C::Scalar>
where
C: Premultiply,
PreAlpha<C>: Compose,
{
#[inline]
fn over(self, other: Self) -> Self {
self.premultiply().over(other.premultiply()).unpremultiply()
}
#[inline]
fn inside(self, other: Self) -> Self {
self.premultiply()
.inside(other.premultiply())
.unpremultiply()
}
#[inline]
fn outside(self, other: Self) -> Self {
self.premultiply()
.outside(other.premultiply())
.unpremultiply()
}
#[inline]
fn atop(self, other: Self) -> Self {
self.premultiply().atop(other.premultiply()).unpremultiply()
}
#[inline]
fn xor(self, other: Self) -> Self {
self.premultiply().xor(other.premultiply()).unpremultiply()
}
#[inline]
fn plus(self, other: Self) -> Self {
self.premultiply().plus(other.premultiply()).unpremultiply()
}
}
impl<C> Compose for C
where
C: Premultiply,
C::Scalar: Stimulus,
PreAlpha<C>: Compose,
{
#[inline]
fn over(self, other: Self) -> Self {
PreAlpha::new_opaque(self)
.over(PreAlpha::new_opaque(other))
.unpremultiply()
.color
}
#[inline]
fn inside(self, other: Self) -> Self {
PreAlpha::new_opaque(self)
.inside(PreAlpha::new_opaque(other))
.unpremultiply()
.color
}
#[inline]
fn outside(self, other: Self) -> Self {
PreAlpha::new_opaque(self)
.outside(PreAlpha::new_opaque(other))
.unpremultiply()
.color
}
#[inline]
fn atop(self, other: Self) -> Self {
PreAlpha::new_opaque(self)
.atop(PreAlpha::new_opaque(other))
.unpremultiply()
.color
}
#[inline]
fn xor(self, other: Self) -> Self {
PreAlpha::new_opaque(self)
.xor(PreAlpha::new_opaque(other))
.unpremultiply()
.color
}
#[inline]
fn plus(self, other: Self) -> Self {
PreAlpha::new_opaque(self)
.plus(PreAlpha::new_opaque(other))
.unpremultiply()
.color
}
}