1use crate::context::CommandContext;
2use crate::version::Api;
3use crate::version::Version;
4
5use crate::DrawError;
6use crate::gl;
7
8#[derive(Copy, Clone, Debug, PartialEq)]
17pub struct Blend {
18 pub color: BlendingFunction,
20 pub alpha: BlendingFunction,
22 pub constant_value: (f32, f32, f32, f32),
24}
25
26impl Blend {
27 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#[derive(Clone, Copy, Debug, PartialEq, Eq)]
55pub enum BlendingFunction {
56 AlwaysReplace,
63
64 Min,
70
71 Max,
77
78 Addition {
85 source: LinearBlendingFactor,
87
88 destination: LinearBlendingFactor,
90 },
91
92 Subtraction {
99 source: LinearBlendingFactor,
101
102 destination: LinearBlendingFactor,
104 },
105
106 ReverseSubtraction {
113 source: LinearBlendingFactor,
115
116 destination: LinearBlendingFactor,
118 },
119}
120
121#[derive(Clone, Copy, Debug, PartialEq, Eq)]
123pub enum LinearBlendingFactor {
124 Zero,
127
128 One,
131
132 SourceColor,
137
138 OneMinusSourceColor,
140
141 DestinationColor,
146
147 OneMinusDestinationColor,
149
150 SourceAlpha,
152
153 SourceAlphaSaturate,
156
157 OneMinusSourceAlpha,
159
160 DestinationAlpha,
162
163 OneMinusDestinationAlpha,
166
167 ConstantColor,
170
171 OneMinusConstantColor,
174
175 ConstantAlpha,
177
178 OneMinusConstantAlpha,
181
182 SourceOneColor,
188
189 OneMinusSourceOneColor,
191
192 SourceOneAlpha,
194
195 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 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 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 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 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}