Skip to main content

golem/
blend.rs

1//! Various options to specify how to combine pixels with what they draw over
2//!
3//! GPU blending follows the equation:
4//!
5//! `Output = Operation(SourceFunction(Source), DestFunction(Destination))`
6//!
7//! The `Operation` is controlled by [`BlendEquation`], and the
8//! `SourceFunction` and `DestFunction` are controlled by [`BlendFunction`]. Both of these are
9//! combined to form a [`BlendMode`], which can be applied by [`Context::set_blend_mode`].
10//!
11//! The default [`BlendMode`] produces the result of:
12//!
13//! `Output = Source.Alpha * Source + (1 - Source.Alpha) * Destination`
14//!
15//! and looks like:
16//!
17//! ```no_run
18//! # use golem::blend::{
19//! #   BlendMode, BlendInput, BlendFactor, BlendChannel, BlendOperation, BlendEquation,
20//! #   BlendFunction,
21//! # };
22//! # fn test() -> BlendMode {
23//! BlendMode {
24//!     equation: BlendEquation::Same(BlendOperation::Add),
25//!     function: BlendFunction::Same {
26//!         source: BlendFactor::Color {
27//!             input: BlendInput::Source,
28//!             channel: BlendChannel::Alpha,
29//!             is_inverse: false,
30//!         },
31//!         destination: BlendFactor::Color {
32//!             input: BlendInput::Source,
33//!             channel: BlendChannel::Alpha,
34//!             is_inverse: true,
35//!         },
36//!     },
37//!     global_color: [0.0; 4]
38//! }
39//! # }
40//! ```
41//!
42//! For more information, see the documentation for the individual Blend enums.
43//!
44//! [`Context::set_blend_mode`]: crate::Context::set_blend_mode
45
46/// The state of the blend pipeline
47///
48/// See [`Context::set_blend_mode`]
49///
50/// [`Context::set_blend_mode`]: crate::Context::set_blend_mode
51#[derive(Copy, Clone, Debug, PartialEq)]
52pub struct BlendMode {
53    /// How to combine the source and destination
54    pub equation: BlendEquation,
55    /// How to transform the inputs to the `equation`
56    pub function: BlendFunction,
57    /// A color in blending that's neither the source nor destination, specified as [R, G, B, A]
58    ///
59    /// This provides the value for [`BlendInput::GlobalBlend`]
60    pub global_color: [f32; 4],
61}
62
63impl Default for BlendMode {
64    fn default() -> BlendMode {
65        BlendMode {
66            equation: BlendEquation::default(),
67            function: BlendFunction::default(),
68            global_color: [0.0; 4],
69        }
70    }
71}
72
73/// How to combine the values when blending
74///
75/// Almost all the time you'll want `BlendEquation::Same(BlendOperation::Add)`, but there are cases
76/// where other blend equatiosn come in handy.
77#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
78pub enum BlendEquation {
79    /// Apply the same equation to the color and alpha of the blended pixels
80    Same(BlendOperation),
81    /// Apply a different equation to the color and alpha channel of the blended pixels
82    Separate {
83        color: BlendOperation,
84        alpha: BlendOperation,
85    },
86}
87
88impl Default for BlendEquation {
89    fn default() -> BlendEquation {
90        BlendEquation::Same(BlendOperation::Add)
91    }
92}
93
94/// The operation to apply to the pixels during blending
95#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
96pub enum BlendOperation {
97    /// Output = Source + Destination
98    Add,
99    /// Output = Source - Destination
100    Subtract,
101    /// Output = Destination - Source
102    ReverseSubtract,
103    /// Output = Max(Source, Destination)
104    Max,
105    /// Output = Min(Source, Destination)
106    Min,
107}
108
109impl BlendOperation {
110    pub(crate) fn to_gl(self) -> u32 {
111        use BlendOperation::*;
112        match self {
113            Add => glow::FUNC_ADD,
114            Subtract => glow::FUNC_SUBTRACT,
115            ReverseSubtract => glow::FUNC_REVERSE_SUBTRACT,
116            Max => glow::MAX,
117            Min => glow::MIN,
118        }
119    }
120}
121
122/// The blend function controls how the source and destination are transformed
123///
124/// Before being passed to the [`BlendEquation`], the source and destination are multiplied by the
125/// value determined by `BlendFunction`.
126#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
127pub enum BlendFunction {
128    /// Use the same [`BlendFactor`] on the color and alpha channels
129    Same {
130        source: BlendFactor,
131        destination: BlendFactor,
132    },
133    /// Use different [`BlendFactor`]s on the color and alpha channels
134    Separate {
135        source_color: BlendFactor,
136        source_alpha: BlendFactor,
137        destination_color: BlendFactor,
138        destination_alpha: BlendFactor,
139    },
140}
141
142impl Default for BlendFunction {
143    fn default() -> BlendFunction {
144        BlendFunction::Same {
145            source: BlendFactor::Color {
146                input: BlendInput::Source,
147                channel: BlendChannel::Alpha,
148                is_inverse: false,
149            },
150            destination: BlendFactor::Color {
151                input: BlendInput::Source,
152                channel: BlendChannel::Alpha,
153                is_inverse: true,
154            },
155        }
156    }
157}
158
159/// The various coefficients to multiply the color inputs by
160#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
161pub enum BlendFactor {
162    Zero,
163    One,
164    Color {
165        /// Which source of color
166        input: BlendInput,
167        /// Which channel of the `input` to use
168        channel: BlendChannel,
169        /// Whether to use (1 - Value) instead of Value
170        is_inverse: bool,
171    },
172}
173
174impl BlendFactor {
175    pub(crate) fn to_gl(self) -> u32 {
176        use BlendChannel::*;
177        use BlendFactor::{Color as Col, One, Zero};
178        use BlendInput::*;
179
180        match self {
181            Zero => glow::ZERO,
182            One => glow::ONE,
183            Col {
184                input: Source,
185                channel: Color,
186                is_inverse: false,
187            } => glow::SRC_COLOR,
188            Col {
189                input: Source,
190                channel: Color,
191                is_inverse: true,
192            } => glow::ONE_MINUS_SRC_COLOR,
193            Col {
194                input: Source,
195                channel: Alpha,
196                is_inverse: false,
197            } => glow::SRC_ALPHA,
198            Col {
199                input: Source,
200                channel: Alpha,
201                is_inverse: true,
202            } => glow::ONE_MINUS_SRC_ALPHA,
203            Col {
204                input: Destination,
205                channel: Color,
206                is_inverse: false,
207            } => glow::DST_COLOR,
208            Col {
209                input: Destination,
210                channel: Color,
211                is_inverse: true,
212            } => glow::ONE_MINUS_DST_COLOR,
213            Col {
214                input: Destination,
215                channel: Alpha,
216                is_inverse: false,
217            } => glow::DST_ALPHA,
218            Col {
219                input: Destination,
220                channel: Alpha,
221                is_inverse: true,
222            } => glow::ONE_MINUS_DST_ALPHA,
223            Col {
224                input: GlobalBlend,
225                channel: Color,
226                is_inverse: false,
227            } => glow::CONSTANT_COLOR,
228            Col {
229                input: GlobalBlend,
230                channel: Color,
231                is_inverse: true,
232            } => glow::ONE_MINUS_CONSTANT_COLOR,
233            Col {
234                input: GlobalBlend,
235                channel: Alpha,
236                is_inverse: false,
237            } => glow::CONSTANT_ALPHA,
238            Col {
239                input: GlobalBlend,
240                channel: Alpha,
241                is_inverse: true,
242            } => glow::ONE_MINUS_CONSTANT_ALPHA,
243        }
244    }
245}
246
247/// A color to pull from when blending
248#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
249pub enum BlendInput {
250    /// The pixel that is being drawn
251    Source,
252    /// The pixel that is being replaced
253    Destination,
254    /// The color supplied to [`BlendMode::global_color`]
255    GlobalBlend,
256}
257
258/// Which part of the [`BlendInput`] to pull from
259#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
260pub enum BlendChannel {
261    /// The RGB component when using separate functions, or RGBA otherwise
262    Color,
263    /// The Alpha component
264    Alpha,
265}