palette/blend/
equations.rs

1use core::ops::{Mul, Sub};
2
3use crate::{
4    blend::{BlendFunction, PreAlpha},
5    cast::ArrayCast,
6    num::{Arithmetics, IsValidDivisor, MinMax, One, Real, Sqrt, Zero},
7};
8
9use super::{zip_colors, Premultiply};
10
11/// A pair of blending equations and corresponding parameters.
12///
13/// The `Equations` type is similar to how blending works in OpenGL, where a
14/// blend function has can be written as `e(sp * S, dp * D)`. `e` is the
15/// equation (like `s + d`), `sp` and `dp` are the source and destination
16/// parameters, and `S` and `D` are the source and destination colors.
17#[derive(Clone, Copy, PartialEq, Eq, Debug)]
18pub struct Equations {
19    /// The equation for the color components.
20    pub color_equation: Equation,
21
22    /// The equation for the alpha component.
23    pub alpha_equation: Equation,
24
25    /// The parameters for the color components.
26    pub color_parameters: Parameters,
27
28    /// The parameters for the alpha component.
29    pub alpha_parameters: Parameters,
30}
31
32impl Equations {
33    /// Create a pair of blending equations, where all the parameters are
34    /// `One`.
35    pub fn from_equations(color: Equation, alpha: Equation) -> Equations {
36        Equations {
37            color_equation: color,
38            alpha_equation: alpha,
39            color_parameters: Parameters {
40                source: Parameter::One,
41                destination: Parameter::One,
42            },
43            alpha_parameters: Parameters {
44                source: Parameter::One,
45                destination: Parameter::One,
46            },
47        }
48    }
49
50    /// Create a pair of additive blending equations with the provided
51    /// parameters.
52    pub fn from_parameters(source: Parameter, destination: Parameter) -> Equations {
53        Equations {
54            color_equation: Equation::Add,
55            alpha_equation: Equation::Add,
56            color_parameters: Parameters {
57                source,
58                destination,
59            },
60            alpha_parameters: Parameters {
61                source,
62                destination,
63            },
64        }
65    }
66}
67
68impl<C, S, const N: usize, const M: usize> BlendFunction<C> for Equations
69where
70    C: Clone
71        + Premultiply<Scalar = S>
72        + Mul<Output = C>
73        + Mul<S, Output = C>
74        + ArrayCast<Array = [S; N]>,
75    PreAlpha<C>: ArrayCast<Array = [S; M]>,
76    S: Real + One + Zero + MinMax + Sqrt + IsValidDivisor + Arithmetics + Clone,
77{
78    fn apply_to(self, source: PreAlpha<C>, destination: PreAlpha<C>) -> PreAlpha<C> {
79        let (src_color, mut dst_color) =
80            if matches!(self.color_equation, Equation::Min | Equation::Max) {
81                (source.color.clone(), destination.color.clone())
82            } else {
83                let col_src_param = self
84                    .color_parameters
85                    .source
86                    .apply_to(source.clone(), destination.clone());
87                let col_dst_param = self
88                    .color_parameters
89                    .destination
90                    .apply_to(source.clone(), destination.clone());
91
92                (
93                    col_src_param.mul_color(source.color.clone()),
94                    col_dst_param.mul_color(destination.color.clone()),
95                )
96            };
97
98        let (src_alpha, dst_alpha) = if matches!(self.alpha_equation, Equation::Min | Equation::Max)
99        {
100            (source.alpha, destination.alpha)
101        } else {
102            let alpha_src_param = self
103                .alpha_parameters
104                .source
105                .apply_to(source.clone(), destination.clone());
106            let alpha_dst_param = self
107                .alpha_parameters
108                .destination
109                .apply_to(source.clone(), destination.clone());
110
111            (
112                alpha_src_param.mul_constant(source.alpha),
113                alpha_dst_param.mul_constant(destination.alpha),
114            )
115        };
116
117        let color_op = match self.color_equation {
118            Equation::Add => |src, dst| src + dst,
119            Equation::Subtract => |src, dst| src - dst,
120            Equation::ReverseSubtract => |src, dst| dst - src,
121            Equation::Min => MinMax::min,
122            Equation::Max => MinMax::max,
123        };
124
125        let alpha_op = match self.alpha_equation {
126            Equation::Add => |src, dst| src + dst,
127            Equation::Subtract => |src, dst| src - dst,
128            Equation::ReverseSubtract => |src, dst| dst - src,
129            Equation::Min => MinMax::min,
130            Equation::Max => MinMax::max,
131        };
132
133        for (src, dst) in zip_colors(src_color, &mut dst_color) {
134            *dst = color_op(src, dst.clone());
135        }
136
137        PreAlpha {
138            color: dst_color,
139            alpha: alpha_op(src_alpha, dst_alpha),
140        }
141    }
142}
143
144/// A blending equation.
145#[derive(Clone, Copy, PartialEq, Eq, Debug)]
146pub enum Equation {
147    /// Add the source and destination, according to `sp * S + dp * D`.
148    Add,
149
150    /// Subtract the destination from the source, according to `sp * S - dp *
151    /// D`.
152    Subtract,
153
154    /// Subtract the source from the destination, according to `dp * D - sp *
155    /// S`.
156    ReverseSubtract,
157
158    /// Create a color where each component is the smallest of each of the
159    /// source and destination components. A.k.a. component wise min. The
160    /// parameters are ignored.
161    Min,
162
163    /// Create a color where each component is the largest of each of the
164    /// source and destination components. A.k.a. component wise max. The
165    /// parameters are ignored.
166    Max,
167}
168
169/// A pair of source and destination parameters.
170#[derive(Clone, Copy, PartialEq, Eq, Debug)]
171pub struct Parameters {
172    /// The source parameter.
173    pub source: Parameter,
174
175    /// The destination parameter.
176    pub destination: Parameter,
177}
178
179/// A blending parameter.
180#[derive(Clone, Copy, PartialEq, Eq, Debug)]
181pub enum Parameter {
182    /// A simple 1.
183    One,
184
185    /// A simple 0.
186    Zero,
187
188    /// The source color, or alpha.
189    SourceColor,
190
191    /// One minus the source color, or alpha.
192    OneMinusSourceColor,
193
194    /// The destination color, or alpha.
195    DestinationColor,
196
197    /// One minus the destination color, or alpha.
198    OneMinusDestinationColor,
199
200    /// The source alpha.
201    SourceAlpha,
202
203    /// One minus the source alpha.
204    OneMinusSourceAlpha,
205
206    /// The destination alpha.
207    DestinationAlpha,
208
209    /// One minus the destination alpha.
210    OneMinusDestinationAlpha,
211}
212
213impl Parameter {
214    fn apply_to<C, T, const N: usize>(
215        &self,
216        source: PreAlpha<C>,
217        destination: PreAlpha<C>,
218    ) -> ParamOut<C>
219    where
220        C: Premultiply<Scalar = T>,
221        PreAlpha<C>: ArrayCast<Array = [T; N]>,
222        T: Real + One + Zero + Sub<Output = T>,
223    {
224        match *self {
225            Parameter::One => ParamOut::Constant(T::one()),
226            Parameter::Zero => ParamOut::Constant(T::zero()),
227            Parameter::SourceColor => ParamOut::Color(source),
228            Parameter::OneMinusSourceColor => {
229                ParamOut::Color(<[T; N]>::from(source).map(|a| T::one() - a).into())
230            }
231            Parameter::DestinationColor => ParamOut::Color(destination),
232            Parameter::OneMinusDestinationColor => {
233                ParamOut::Color(<[T; N]>::from(destination).map(|a| T::one() - a).into())
234            }
235            Parameter::SourceAlpha => ParamOut::Constant(source.alpha),
236            Parameter::OneMinusSourceAlpha => ParamOut::Constant(T::one() - source.alpha),
237            Parameter::DestinationAlpha => ParamOut::Constant(destination.alpha),
238            Parameter::OneMinusDestinationAlpha => ParamOut::Constant(T::one() - destination.alpha),
239        }
240    }
241}
242
243enum ParamOut<C: Premultiply> {
244    Color(PreAlpha<C>),
245    Constant(C::Scalar),
246}
247
248impl<C, T> ParamOut<C>
249where
250    C: Mul<Output = C> + Mul<T, Output = C> + Premultiply<Scalar = T>,
251    T: Mul<Output = T> + Clone,
252{
253    fn mul_constant(self, other: T) -> T {
254        match self {
255            ParamOut::Color(c) => c.alpha * other,
256            ParamOut::Constant(c) => c * other,
257        }
258    }
259
260    fn mul_color(self, other: C) -> C {
261        match self {
262            ParamOut::Color(c) => other * c.color,
263            ParamOut::Constant(c) => other * c,
264        }
265    }
266}