glium/draw_parameters/
blend.rs

1use crate::context::CommandContext;
2use crate::version::Api;
3use crate::version::Version;
4
5use crate::DrawError;
6use crate::gl;
7
8/// Blend effect that the GPU will use for blending.
9///
10/// Blending happens at the end of the rendering process, when the GPU wants to write the
11/// pixels over pixels that already exist in the framebuffer. The blending function allows
12/// you to choose how it should merge the two.
13///
14/// If you want to add transparent objects one over another, use
15/// `Blend::alpha_blending()`.
16#[derive(Copy, Clone, Debug, PartialEq)]
17pub struct Blend {
18    /// The blending function for color channels.
19    pub color: BlendingFunction,
20    /// The blending function for alpha channels.
21    pub alpha: BlendingFunction,
22    /// A constant color that can be used in the blending functions.
23    pub constant_value: (f32, f32, f32, f32),
24}
25
26impl Blend {
27    /// Returns a blend effect to add transparent objects over others.
28    pub fn alpha_blending() -> Blend {
29        Blend {
30            color: BlendingFunction::Addition {
31                source: LinearBlendingFactor::SourceAlpha,
32                destination: LinearBlendingFactor::OneMinusSourceAlpha,
33            },
34            alpha: BlendingFunction::Addition {
35                source: LinearBlendingFactor::SourceAlpha,
36                destination: LinearBlendingFactor::OneMinusSourceAlpha
37            },
38            constant_value: (0.0, 0.0, 0.0, 0.0)
39        }
40    }
41}
42
43impl Default for Blend {
44    fn default() -> Blend {
45        Blend {
46            color: BlendingFunction::AlwaysReplace,
47            alpha: BlendingFunction::AlwaysReplace,
48            constant_value: (1.0, 1.0, 1.0, 1.0),
49        }
50    }
51}
52
53/// Function that the GPU will use for blending.
54#[derive(Clone, Copy, Debug, PartialEq, Eq)]
55pub enum BlendingFunction {
56    /// Simply overwrite the destination pixel with the source pixel.
57    ///
58    /// The alpha channels are simply ignored. This is the default mode.
59    ///
60    /// For example writing `(0.5, 0.9, 0.4, 0.2)` over `(0.9, 0.1, 0.4, 0.3)` will
61    /// result in `(0.5, 0.9, 0.4, 0.2)`.
62    AlwaysReplace,
63
64    /// For each individual component (red, green, blue, and alpha), the minimum value is chosen
65    /// between the source and the destination.
66    ///
67    /// For example writing `(0.5, 0.9, 0.4, 0.2)` over `(0.9, 0.1, 0.4, 0.3)` will
68    /// result in `(0.5, 0.1, 0.4, 0.2)`.
69    Min,
70
71    /// For each individual component (red, green, blue, and alpha), the maximum value is chosen
72    /// between the source and the destination.
73    ///
74    /// For example writing `(0.5, 0.9, 0.4, 0.2)` over `(0.9, 0.1, 0.4, 0.3)` will
75    /// result in `(0.9, 0.9, 0.4, 0.3)`.
76    Max,
77
78    /// For each individual component (red, green, blue, and alpha), a weighted addition
79    /// between the source and the destination.
80    ///
81    /// The result is equal to `source_component * source_factor + dest_component * dest_factor`,
82    /// where `source_factor` and `dest_factor` are the values of `source` and `destination` of
83    /// this enum.
84    Addition {
85        /// The factor to apply to the source pixel.
86        source: LinearBlendingFactor,
87
88        /// The factor to apply to the destination pixel.
89        destination: LinearBlendingFactor,
90    },
91
92    /// For each individual component (red, green, blue, and alpha), a weighted subtraction
93    /// of the source by the destination.
94    ///
95    /// The result is equal to `source_component * source_factor - dest_component * dest_factor`,
96    /// where `source_factor` and `dest_factor` are the values of `source` and `destination` of
97    /// this enum.
98    Subtraction {
99        /// The factor to apply to the source pixel.
100        source: LinearBlendingFactor,
101
102        /// The factor to apply to the destination pixel.
103        destination: LinearBlendingFactor,
104    },
105
106    /// For each individual component (red, green, blue, and alpha), a weighted subtraction
107    /// of the destination by the source.
108    ///
109    /// The result is equal to `-source_component * source_factor + dest_component * dest_factor`,
110    /// where `source_factor` and `dest_factor` are the values of `source` and `destination` of
111    /// this enum.
112    ReverseSubtraction {
113        /// The factor to apply to the source pixel.
114        source: LinearBlendingFactor,
115
116        /// The factor to apply to the destination pixel.
117        destination: LinearBlendingFactor,
118    },
119}
120
121/// Indicates which value to multiply each component with.
122#[derive(Clone, Copy, Debug, PartialEq, Eq)]
123pub enum LinearBlendingFactor {
124    /// Multiply the source or destination component by zero, which always
125    /// gives `0.0`.
126    Zero,
127
128    /// Multiply the source or destination component by one, which always
129    /// gives you the original value.
130    One,
131
132    /// Multiply the source or destination component by its corresponding value
133    /// in the source.
134    ///
135    /// If you apply this to the source components, you get the values squared.
136    SourceColor,
137
138    /// Equivalent to `1 - SourceColor`.
139    OneMinusSourceColor,
140
141    /// Multiply the source or destination component by its corresponding value
142    /// in the destination.
143    ///
144    /// If you apply this to the destination components, you get the values squared.
145    DestinationColor,
146
147    /// Equivalent to `1 - DestinationColor`.
148    OneMinusDestinationColor,
149
150    /// Multiply the source or destination component by the alpha value of the source.
151    SourceAlpha,
152
153    /// Multiply the source or destination component by the smallest value of
154    /// `SourceAlpha` and `1 - DestinationAlpha`.
155    SourceAlphaSaturate,
156
157    /// Multiply the source or destination component by `1.0` minus the alpha value of the source.
158    OneMinusSourceAlpha,
159
160    /// Multiply the source or destination component by the alpha value of the destination.
161    DestinationAlpha,
162
163    /// Multiply the source or destination component by `1.0` minus the alpha value of the
164    /// destination.
165    OneMinusDestinationAlpha,
166
167    /// Multiply the source or destination component by the corresponding value
168    /// in `Blend::const_value`.
169    ConstantColor,
170
171    /// Multiply the source or destination component by `1.0` minus the corresponding
172    /// value in `Blend::const_value`.
173    OneMinusConstantColor,
174
175    /// Multiply the source or destination component by the alpha value of `Blend::const_value`.
176    ConstantAlpha,
177
178    /// Multiply the source or destination component by `1.0` minus the alpha value of
179    /// `Blend::const_value`.
180    OneMinusConstantAlpha,
181
182    /// Multiply the source or destination component by its corresponding value
183    /// in source index one (you need to explicitly specify `layout(location=0, index=1)`
184    /// to bind it in your shader.
185    /// This is useful in Dual Source Blending
186    /// <https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_blend_func_extended.txt>
187    SourceOneColor,
188
189    /// Equivalent to `1 - SourceOneColor`.
190    OneMinusSourceOneColor,
191
192    /// Multiply the source or destination component by the alpha value of source index 1.
193    SourceOneAlpha,
194
195    /// Multiply the source or destination component by `1.0` minus the alpha value of source index 1.
196    OneMinusSourceOneAlpha,
197}
198
199impl LinearBlendingFactor {
200    fn to_glenum(&self) -> gl::types::GLenum {
201        match *self {
202            LinearBlendingFactor::Zero => gl::ZERO,
203            LinearBlendingFactor::One => gl::ONE,
204            LinearBlendingFactor::SourceColor => gl::SRC_COLOR,
205            LinearBlendingFactor::OneMinusSourceColor => gl::ONE_MINUS_SRC_COLOR,
206            LinearBlendingFactor::DestinationColor => gl::DST_COLOR,
207            LinearBlendingFactor::OneMinusDestinationColor => gl::ONE_MINUS_DST_COLOR,
208            LinearBlendingFactor::SourceAlpha => gl::SRC_ALPHA,
209            LinearBlendingFactor::OneMinusSourceAlpha => gl::ONE_MINUS_SRC_ALPHA,
210            LinearBlendingFactor::DestinationAlpha => gl::DST_ALPHA,
211            LinearBlendingFactor::OneMinusDestinationAlpha => gl::ONE_MINUS_DST_ALPHA,
212            LinearBlendingFactor::SourceAlphaSaturate => gl::SRC_ALPHA_SATURATE,
213            LinearBlendingFactor::ConstantColor => gl::CONSTANT_COLOR,
214            LinearBlendingFactor::OneMinusConstantColor => gl::ONE_MINUS_CONSTANT_COLOR,
215            LinearBlendingFactor::ConstantAlpha => gl::CONSTANT_ALPHA,
216            LinearBlendingFactor::OneMinusConstantAlpha => gl::ONE_MINUS_CONSTANT_ALPHA,
217            LinearBlendingFactor::SourceOneColor => gl::SRC1_COLOR,
218            LinearBlendingFactor::OneMinusSourceOneColor => gl::ONE_MINUS_SRC1_COLOR,
219            LinearBlendingFactor::SourceOneAlpha => gl::SRC1_ALPHA,
220            LinearBlendingFactor::OneMinusSourceOneAlpha => gl::ONE_MINUS_SRC1_ALPHA,
221
222        }
223    }
224}
225
226pub fn sync_blending(ctxt: &mut CommandContext<'_>, blend: Blend) -> Result<(), DrawError> {
227    #[inline(always)]
228    fn blend_eq(ctxt: &mut CommandContext<'_>, blending_function: BlendingFunction)
229                -> Result<gl::types::GLenum, DrawError>
230    {
231        match blending_function {
232            BlendingFunction::AlwaysReplace |
233            BlendingFunction::Addition { .. } => Ok(gl::FUNC_ADD),
234            BlendingFunction::Subtraction { .. } => Ok(gl::FUNC_SUBTRACT),
235            BlendingFunction::ReverseSubtraction { .. } => Ok(gl::FUNC_REVERSE_SUBTRACT),
236
237            BlendingFunction::Min => {
238                if ctxt.version <= &Version(Api::GlEs, 2, 0) &&
239                   !ctxt.extensions.gl_ext_blend_minmax
240                {
241                    Err(DrawError::BlendingParameterNotSupported)
242                } else {
243                    Ok(gl::MIN)
244                }
245            },
246
247            BlendingFunction::Max => {
248                if ctxt.version <= &Version(Api::GlEs, 2, 0) &&
249                   !ctxt.extensions.gl_ext_blend_minmax
250                {
251                    Err(DrawError::BlendingParameterNotSupported)
252                } else {
253                    Ok(gl::MAX)
254                }
255            },
256        }
257    }
258
259    #[inline(always)]
260    fn blending_factors(blending_function: BlendingFunction)
261                        -> Option<(LinearBlendingFactor, LinearBlendingFactor)>
262    {
263        match blending_function {
264            BlendingFunction::AlwaysReplace |
265            BlendingFunction::Min |
266            BlendingFunction::Max => None,
267            BlendingFunction::Addition { source, destination } =>
268                Some((source, destination)),
269            BlendingFunction::Subtraction { source, destination } =>
270                Some((source, destination)),
271            BlendingFunction::ReverseSubtraction { source, destination } =>
272                Some((source, destination)),
273        }
274    }
275
276    if let (BlendingFunction::AlwaysReplace, BlendingFunction::AlwaysReplace) =
277           (blend.color, blend.alpha)
278    {
279        // Both color and alpha always replace. This equals no blending.
280        if ctxt.state.enabled_blend {
281            unsafe { ctxt.gl.Disable(gl::BLEND); }
282            ctxt.state.enabled_blend = false;
283        }
284
285    } else {
286        if !ctxt.state.enabled_blend {
287            unsafe { ctxt.gl.Enable(gl::BLEND); }
288            ctxt.state.enabled_blend = true;
289        }
290
291        let (color_eq, alpha_eq) = (blend_eq(ctxt, blend.color)?,
292                                    blend_eq(ctxt, blend.alpha)?);
293        if ctxt.state.blend_equation != (color_eq, alpha_eq) {
294            unsafe { ctxt.gl.BlendEquationSeparate(color_eq, alpha_eq); }
295            ctxt.state.blend_equation = (color_eq, alpha_eq);
296        }
297
298        // Map to dummy factors if the blending equation does not use the factors.
299        let (color_factor_src, color_factor_dst) = blending_factors(blend.color)
300            .unwrap_or((LinearBlendingFactor::One, LinearBlendingFactor::Zero));
301        let (alpha_factor_src, alpha_factor_dst) = blending_factors(blend.alpha)
302            .unwrap_or((LinearBlendingFactor::One, LinearBlendingFactor::Zero));
303
304        // Updating the blending color if necessary.
305        if (color_factor_src == LinearBlendingFactor::ConstantColor ||
306           color_factor_src == LinearBlendingFactor::OneMinusConstantColor ||
307           color_factor_dst == LinearBlendingFactor::ConstantColor ||
308           color_factor_dst == LinearBlendingFactor::OneMinusConstantColor ||
309           alpha_factor_src == LinearBlendingFactor::ConstantColor ||
310           alpha_factor_src == LinearBlendingFactor::OneMinusConstantColor ||
311           alpha_factor_dst == LinearBlendingFactor::ConstantColor ||
312           alpha_factor_dst == LinearBlendingFactor::OneMinusConstantColor ||
313           color_factor_src == LinearBlendingFactor::ConstantAlpha ||
314           color_factor_src == LinearBlendingFactor::OneMinusConstantAlpha ||
315           color_factor_dst == LinearBlendingFactor::ConstantAlpha ||
316           color_factor_dst == LinearBlendingFactor::OneMinusConstantAlpha ||
317           alpha_factor_src == LinearBlendingFactor::ConstantAlpha ||
318           alpha_factor_src == LinearBlendingFactor::OneMinusConstantAlpha ||
319           alpha_factor_dst == LinearBlendingFactor::ConstantAlpha ||
320           alpha_factor_dst == LinearBlendingFactor::OneMinusConstantAlpha) && ctxt.state.blend_color != blend.constant_value {
321            let (r, g, b, a) = blend.constant_value;
322            unsafe { ctxt.gl.BlendColor(r, g, b, a); }
323            ctxt.state.blend_color = blend.constant_value;
324        }
325
326        // Updating the blending function if necessary.
327        let color_factor_src = color_factor_src.to_glenum();
328        let color_factor_dst = color_factor_dst.to_glenum();
329        let alpha_factor_src = alpha_factor_src.to_glenum();
330        let alpha_factor_dst = alpha_factor_dst.to_glenum();
331        if ctxt.state.blend_func != (color_factor_src, color_factor_dst,
332                                     alpha_factor_src, alpha_factor_dst)
333        {
334            unsafe {
335                ctxt.gl.BlendFuncSeparate(color_factor_src, color_factor_dst,
336                                          alpha_factor_src, alpha_factor_dst);
337            }
338
339            ctxt.state.blend_func = (color_factor_src, color_factor_dst,
340                                     alpha_factor_src, alpha_factor_dst);
341        }
342    }
343
344    Ok(())
345}