palette/
blend.rs

1//! Color blending and blending equations.
2//!
3//! Palette offers both OpenGL style blending equations, as well as most of the
4//! SVG composition operators (also common in photo manipulation software). The
5//! composition operators are all implemented in the [`Compose`] and [`Blend`]
6//! traits, and ready to use with any appropriate color type:
7//!
8//! ```
9//! use palette::{blend::Blend, LinSrgba};
10//!
11//! let a = LinSrgba::new(0.2, 0.5, 0.1, 0.8);
12//! let b = LinSrgba::new(0.6, 0.3, 0.5, 0.1);
13//! let c = a.overlay(b);
14//! ```
15//!
16//! Blending equations can be defined using the [`Equations`] type, which is
17//! then passed to the `blend` function, from the `Blend` trait:
18//!
19//! ```
20//! use palette::LinSrgba;
21//! use palette::blend::{BlendWith, Equations, Parameter};
22//!
23//! let blend_mode = Equations::from_parameters(
24//!    Parameter::SourceAlpha,
25//!    Parameter::OneMinusSourceAlpha
26//! );
27//!
28//! let a = LinSrgba::new(0.2, 0.5, 0.1, 0.8);
29//! let b = LinSrgba::new(0.6, 0.3, 0.5, 0.1);
30//! let c = a.blend_with(b, blend_mode);
31//! ```
32//!
33//! Note that blending will use [premultiplied alpha](crate::blend::PreAlpha),
34//! which may result in loss of some color information in some cases. One such
35//! case is that a completely transparent resultant color will become black.
36
37use crate::{
38    cast::{self, ArrayCast},
39    clamp,
40    num::{Arithmetics, Clamp, One, Real, Zero},
41    stimulus::Stimulus,
42};
43
44pub use self::{
45    blend::Blend,
46    blend_with::BlendWith,
47    compose::Compose,
48    equations::{Equation, Equations, Parameter, Parameters},
49    pre_alpha::PreAlpha,
50};
51
52#[allow(clippy::module_inception)]
53mod blend;
54mod blend_with;
55mod compose;
56mod equations;
57mod pre_alpha;
58
59#[cfg(test)]
60mod test;
61
62/// A trait for custom blend functions.
63pub trait BlendFunction<C>
64where
65    C: Premultiply,
66{
67    /// Apply this blend function to a pair of colors.
68    #[must_use]
69    fn apply_to(self, source: PreAlpha<C>, destination: PreAlpha<C>) -> PreAlpha<C>;
70}
71
72impl<C, F> BlendFunction<C> for F
73where
74    C: Premultiply,
75    F: FnOnce(PreAlpha<C>, PreAlpha<C>) -> PreAlpha<C>,
76{
77    #[inline]
78    fn apply_to(self, source: PreAlpha<C>, destination: PreAlpha<C>) -> PreAlpha<C> {
79        (self)(source, destination)
80    }
81}
82
83/// Alpha masking and unmasking.
84pub trait Premultiply: Sized {
85    /// The color's component type.
86    type Scalar: Real + Stimulus;
87
88    /// Alpha mask the color.
89    ///
90    /// This is done by multiplying the color's component by `alpha`.
91    #[must_use]
92    fn premultiply(self, alpha: Self::Scalar) -> PreAlpha<Self>;
93
94    /// Alpha unmask the color, resulting in a color and transparency pair.
95    ///
96    /// This is done by dividing the masked color's component by `alpha`, or
97    /// returning a black color if `alpha` is `0`.
98    #[must_use]
99    fn unpremultiply(premultiplied: PreAlpha<Self>) -> (Self, Self::Scalar);
100}
101
102fn blend_alpha<T>(src: T, dst: T) -> T
103where
104    T: Zero + One + Arithmetics + Clamp + Clone,
105{
106    clamp(src.clone() + &dst - src * dst, T::zero(), T::one())
107}
108
109fn zip_colors<'a, C, T, const N: usize>(
110    src: C,
111    dst: &'a mut C,
112) -> impl Iterator<Item = (T, &'a mut T)>
113where
114    C: ArrayCast<Array = [T; N]>,
115    T: 'a,
116{
117    IntoIterator::into_iter(cast::into_array(src)).zip(cast::into_array_mut(dst))
118}