1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
use web_sys::WebGl2RenderingContext as Gl;

use crate::runtime::state::ContextUpdate;
use crate::runtime::Connection;

/// Enumerates the possible blending factors that can be applied to color values during [Blending].
///
/// See the documentation for [Blending] for details on how these blending factors are used and what
/// their effects are.
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum BlendFactor {
    Zero,
    One,
    ConstantColor,
    OneMinusConstantColor,
    ConstantAlpha,
    OneMinusConstantAlpha,
    SourceColor,
    OneMinusSourceColor,
    DestinationColor,
    OneMinusDestinationColor,
    SourceAlpha,
    OneMinusSourceAlpha,
    DestinationAlpha,
    OneMinusDestinationAlpha,
    SourceAlphaSaturate,
}

impl BlendFactor {
    pub(crate) fn id(&self) -> u32 {
        match self {
            BlendFactor::Zero => Gl::ZERO,
            BlendFactor::One => Gl::ONE,
            BlendFactor::ConstantColor => Gl::CONSTANT_COLOR,
            BlendFactor::OneMinusConstantColor => Gl::ONE_MINUS_CONSTANT_COLOR,
            BlendFactor::ConstantAlpha => Gl::CONSTANT_ALPHA,
            BlendFactor::OneMinusConstantAlpha => Gl::ONE_MINUS_CONSTANT_ALPHA,
            BlendFactor::SourceColor => Gl::SRC_COLOR,
            BlendFactor::OneMinusSourceColor => Gl::ONE_MINUS_SRC_COLOR,
            BlendFactor::DestinationColor => Gl::DST_COLOR,
            BlendFactor::OneMinusDestinationColor => Gl::ONE_MINUS_DST_COLOR,
            BlendFactor::SourceAlpha => Gl::SRC_ALPHA,
            BlendFactor::OneMinusSourceAlpha => Gl::ONE_MINUS_SRC_ALPHA,
            BlendFactor::DestinationAlpha => Gl::DST_ALPHA,
            BlendFactor::OneMinusDestinationAlpha => Gl::ONE_MINUS_DST_ALPHA,
            BlendFactor::SourceAlphaSaturate => Gl::SRC_ALPHA_SATURATE,
        }
    }
}

/// Enumerates the available blend equations that can be employed to perform [Blending].
///
/// See the documentation for [Blending] for details on how these equations act.
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum BlendEquation {
    Addition,
    Subtraction,
    ReverseSubtraction,
    Min,
    Max,
}

impl BlendEquation {
    pub(crate) fn id(&self) -> u32 {
        match self {
            BlendEquation::Addition => Gl::FUNC_ADD,
            BlendEquation::Subtraction => Gl::FUNC_SUBTRACT,
            BlendEquation::ReverseSubtraction => Gl::FUNC_REVERSE_SUBTRACT,
            BlendEquation::Min => Gl::MIN,
            BlendEquation::Max => Gl::MAX,
        }
    }
}

/// Provides instructions on how blending should be performed.
///
/// When [Blending] is enabled, a fragment's color output does not merely overwrite the color
/// buffer's current color value for this fragment, but instead these two values are combined using
/// a [BlendEquation]. The new color output for the fragment is referred to as the source color,
/// the value already in the color buffer is referred to as the destination color. Separate blending
/// equations may be used for the RGB portion and for the alpha portion of the color value; the
/// respective blending equations are specified by [color_equation] and [alpha_equation]. The
/// following blending equations are available:
///
/// - [BlendEquation::Addition]: the output of blending `O` is calculated as
///   `O = F_s * S + F_d * D`.
/// - [BlendEquation::Subtraction]: the output of blending `O` is calculated as
///   `O = F_s * S - F_d * D`.
/// - [BlendEquation::ReverseSubtraction]: the output of blending `O` is calculated as
///   `O = F_d * D - F_s * S`.
/// - [BlendEquation::Min]: the output of blending `O` is calculated as `O = min(F_d * D, F_s * S)`.
/// - [BlendEquation::Max]: the output of blending `O` is calculated as `O = max(F_d * D, F_s * S)`.
///
/// Here `S` is the relevant portion of the source value: the [color_equation] will use the red,
/// green and blue components of the source color as `S`, the [alpha_equation] will use the alpha
/// components of the source color as `S`. `D` is the relevant portion of the destination value: the
/// [color_function] will use the red, green and blue components of the destination color as `D`
/// and the [alpha_function] will use the alpha component of the destination color as `D`. `F_s` and
/// `F_d` are [BlendFactor]s for `S` and `D` respectively. The following blend factors are
/// available:
///
/// - [BlendFactor::Zero]: all color components are multiplied by `0`.
/// - [BlendFactor::One]: all color components are multiplied by `1`.
/// - [BlendFactor::ConstantColor]: the value of each color component is multiplied by the value
///   or the corresponding component of the color specified by [constant_color].
/// - [BlendFactor::OneMinusConstantColor]: the value of each color component is multiplied by
///   the value of the corresponding component of the color specified by [constant_color],
///   subtracted from `1`. For example, the red component is multiplied by `1 - R_c`, where `R_c` is
///   the value of the red component of the [constant_color].
/// - [BlendFactor::OneMinusConstantAlpha]: all color components are multiplied by `1 - A_c`,
///   where `A_c` is the value of the alpha component of the color specified by [constant_color].
/// - [BlendFactor::SourceColor]: the value of each color component is multiplied by the value of
///   the corresponding component of the source color.
/// - [BlendFactor::OneMinusSourceColor]: the value of each color component is multiplied by the
///   value of the corresponding component of the source color subtracted from `1`. For example, the
///   red component is multiplied by `1 - R_s`, where `R_s` is the value of the red component of the
///   source color.
/// - [BlendFactor::DestinationColor]: the value of each color component is multiplied by the
///   value of the corresponding component of the destination color.
/// - [BlendFactor::OneMinusDestinationColor]: the value of each color component is multiplied by
///   the value of the corresponding component of the destination color subtracted from `1`. For
///   example, the red component is multiplied by `1 - R_d`, where `R_d` is the value of the red
///   component of the destination color.
/// - [BlendFactor::SourceAlpha]: all color components are multiplied by the value of the alpha
///   component of the source color.
/// - [BlendFactor::OneMinusSourceAlpha]: all color components are multiplied by `1 - A_s`, where
///   `A_s` is the value of the alpha component of the source color.
/// - [BlendFactor::DestinationAlpha]: all color components are multiplied by the value of the
///   alpha component of the destination color.
/// - [BlendFactor::OneMinusDestinationAlpha]: all color components are multiplied by `1 - A_s`,
///   where `A_s` is the value of the alpha component of the destination color.
/// - [BlendFactor::SourceAlphaSaturate]: all color components are multiplied by the smaller of
///   either `A_s` or `1 - A_d`, where `A_s` is the value of the alpha component of the source color
///   and `A_d` is the value of the alpha component of the destination color.
///
/// [Blending] may be instantiated with default values through [Default]:
///
/// ```
/// use web_glitz::pipeline::graphics::{Blending, BlendEquation, BlendFactor};
///
/// assert_eq!(Blending::default(), Blending {
///     constant_color: [0.0, 0.0, 0.0, 0.0],
///     source_color_factor: BlendFactor::One,
///     source_alpha_factor: BlendFactor::One,
///     destination_color_factor: BlendFactor::Zero,
///     destination_alpha_factor: BlendFactor::Zero,
///     color_equation: BlendEquation::Addition,
///     alpha_equation: BlendEquation::Addition
/// });
/// ```
#[derive(Clone, PartialEq, Debug)]
pub struct Blending {
    /// The color used as the constant color when [BlendFactor::ConstantColor],
    /// [BlendFactor::OneMinusConstantColor], [BlendFactor::ConstantAlpha] or
    /// [BlendFactor::OneMinusConstantAlpha] is used as a blending factor.
    ///
    /// Component values are clamped to `[0, 1]`.
    ///
    /// This value is ignored when other [BlendFactor]s are used.
    pub constant_color: [f32; 4],

    /// The [BlendFactor] that the [color_equation] applies to the source value.
    pub source_color_factor: BlendFactor,

    /// The [BlendFactor] that the [alpha_equation] applies to the source value.
    pub source_alpha_factor: BlendFactor,

    /// The [BlendFactor] that the [color_equation] applies to the destination value.
    pub destination_color_factor: BlendFactor,

    /// The [BlendFactor] that the [alpha_equation] applies to the destination value.
    pub destination_alpha_factor: BlendFactor,

    /// The [BlendFactor] used to combine the red, green and blue components of the source and
    /// destination colors.
    pub color_equation: BlendEquation,

    /// The [BlendEquation] used to combine the alpha components of the source and destination
    /// colors.
    pub alpha_equation: BlendEquation,
}

impl Blending {
    pub(crate) fn apply(option: &Option<Self>, connection: &mut Connection) {
        let (gl, state) = unsafe { connection.unpack_mut() };

        match option {
            Some(blend) => {
                state.set_blend_enabled(true).apply(gl).unwrap();
                state
                    .set_blend_color(blend.constant_color)
                    .apply(gl)
                    .unwrap();
                state
                    .set_blend_equations(blend.color_equation, blend.alpha_equation)
                    .apply(gl)
                    .unwrap();
                state
                    .set_blend_func(
                        blend.source_color_factor,
                        blend.destination_color_factor,
                        blend.source_alpha_factor,
                        blend.destination_alpha_factor,
                    )
                    .apply(gl)
                    .unwrap();
            }
            _ => state.set_blend_enabled(false).apply(gl).unwrap(),
        }
    }
}

impl Default for Blending {
    fn default() -> Self {
        Blending {
            constant_color: [0.0; 4],
            source_color_factor: BlendFactor::One,
            source_alpha_factor: BlendFactor::One,
            destination_color_factor: BlendFactor::Zero,
            destination_alpha_factor: BlendFactor::Zero,
            color_equation: BlendEquation::Addition,
            alpha_equation: BlendEquation::Addition,
        }
    }
}