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}