use context::CommandContext;
use version::Api;
use version::Version;
use DrawError;
use gl;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Blend {
pub color: BlendingFunction,
pub alpha: BlendingFunction,
pub constant_value: (f32, f32, f32, f32),
}
impl Blend {
pub fn alpha_blending() -> Blend {
Blend {
color: BlendingFunction::Addition {
source: LinearBlendingFactor::SourceAlpha,
destination: LinearBlendingFactor::OneMinusSourceAlpha,
},
alpha: BlendingFunction::Addition {
source: LinearBlendingFactor::SourceAlpha,
destination: LinearBlendingFactor::OneMinusSourceAlpha
},
constant_value: (0.0, 0.0, 0.0, 0.0)
}
}
}
impl Default for Blend {
fn default() -> Blend {
Blend {
color: BlendingFunction::AlwaysReplace,
alpha: BlendingFunction::AlwaysReplace,
constant_value: (1.0, 1.0, 1.0, 1.0),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BlendingFunction {
AlwaysReplace,
Min,
Max,
Addition {
source: LinearBlendingFactor,
destination: LinearBlendingFactor,
},
Subtraction {
source: LinearBlendingFactor,
destination: LinearBlendingFactor,
},
ReverseSubtraction {
source: LinearBlendingFactor,
destination: LinearBlendingFactor,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum LinearBlendingFactor {
Zero,
One,
SourceColor,
OneMinusSourceColor,
DestinationColor,
OneMinusDestinationColor,
SourceAlpha,
SourceAlphaSaturate,
OneMinusSourceAlpha,
DestinationAlpha,
OneMinusDestinationAlpha,
ConstantColor,
OneMinusConstantColor,
ConstantAlpha,
OneMinusConstantAlpha,
}
impl LinearBlendingFactor {
fn to_glenum(&self) -> gl::types::GLenum {
match *self {
LinearBlendingFactor::Zero => gl::ZERO,
LinearBlendingFactor::One => gl::ONE,
LinearBlendingFactor::SourceColor => gl::SRC_COLOR,
LinearBlendingFactor::OneMinusSourceColor => gl::ONE_MINUS_SRC_COLOR,
LinearBlendingFactor::DestinationColor => gl::DST_COLOR,
LinearBlendingFactor::OneMinusDestinationColor => gl::ONE_MINUS_DST_COLOR,
LinearBlendingFactor::SourceAlpha => gl::SRC_ALPHA,
LinearBlendingFactor::OneMinusSourceAlpha => gl::ONE_MINUS_SRC_ALPHA,
LinearBlendingFactor::DestinationAlpha => gl::DST_ALPHA,
LinearBlendingFactor::OneMinusDestinationAlpha => gl::ONE_MINUS_DST_ALPHA,
LinearBlendingFactor::SourceAlphaSaturate => gl::SRC_ALPHA_SATURATE,
LinearBlendingFactor::ConstantColor => gl::CONSTANT_COLOR,
LinearBlendingFactor::OneMinusConstantColor => gl::ONE_MINUS_CONSTANT_COLOR,
LinearBlendingFactor::ConstantAlpha => gl::CONSTANT_ALPHA,
LinearBlendingFactor::OneMinusConstantAlpha => gl::ONE_MINUS_CONSTANT_ALPHA,
}
}
}
pub fn sync_blending(ctxt: &mut CommandContext, blend: Blend) -> Result<(), DrawError> {
#[inline(always)]
fn blend_eq(ctxt: &mut CommandContext, blending_function: BlendingFunction)
-> Result<gl::types::GLenum, DrawError>
{
match blending_function {
BlendingFunction::AlwaysReplace |
BlendingFunction::Addition { .. } => Ok(gl::FUNC_ADD),
BlendingFunction::Subtraction { .. } => Ok(gl::FUNC_SUBTRACT),
BlendingFunction::ReverseSubtraction { .. } => Ok(gl::FUNC_REVERSE_SUBTRACT),
BlendingFunction::Min => {
if ctxt.version <= &Version(Api::GlEs, 2, 0) &&
!ctxt.extensions.gl_ext_blend_minmax
{
Err(DrawError::BlendingParameterNotSupported)
} else {
Ok(gl::MIN)
}
},
BlendingFunction::Max => {
if ctxt.version <= &Version(Api::GlEs, 2, 0) &&
!ctxt.extensions.gl_ext_blend_minmax
{
Err(DrawError::BlendingParameterNotSupported)
} else {
Ok(gl::MAX)
}
},
}
}
#[inline(always)]
fn blending_factors(blending_function: BlendingFunction)
-> Option<(LinearBlendingFactor, LinearBlendingFactor)>
{
match blending_function {
BlendingFunction::AlwaysReplace |
BlendingFunction::Min |
BlendingFunction::Max => None,
BlendingFunction::Addition { source, destination } =>
Some((source, destination)),
BlendingFunction::Subtraction { source, destination } =>
Some((source, destination)),
BlendingFunction::ReverseSubtraction { source, destination } =>
Some((source, destination)),
}
}
if let (BlendingFunction::AlwaysReplace, BlendingFunction::AlwaysReplace) =
(blend.color, blend.alpha)
{
if ctxt.state.enabled_blend {
unsafe { ctxt.gl.Disable(gl::BLEND); }
ctxt.state.enabled_blend = false;
}
} else {
if !ctxt.state.enabled_blend {
unsafe { ctxt.gl.Enable(gl::BLEND); }
ctxt.state.enabled_blend = true;
}
let (color_eq, alpha_eq) = (blend_eq(ctxt, blend.color)?,
blend_eq(ctxt, blend.alpha)?);
if ctxt.state.blend_equation != (color_eq, alpha_eq) {
unsafe { ctxt.gl.BlendEquationSeparate(color_eq, alpha_eq); }
ctxt.state.blend_equation = (color_eq, alpha_eq);
}
let (color_factor_src, color_factor_dst) = blending_factors(blend.color)
.unwrap_or((LinearBlendingFactor::One, LinearBlendingFactor::Zero));
let (alpha_factor_src, alpha_factor_dst) = blending_factors(blend.alpha)
.unwrap_or((LinearBlendingFactor::One, LinearBlendingFactor::Zero));
if color_factor_src == LinearBlendingFactor::ConstantColor ||
color_factor_src == LinearBlendingFactor::OneMinusConstantColor ||
color_factor_dst == LinearBlendingFactor::ConstantColor ||
color_factor_dst == LinearBlendingFactor::OneMinusConstantColor ||
alpha_factor_src == LinearBlendingFactor::ConstantColor ||
alpha_factor_src == LinearBlendingFactor::OneMinusConstantColor ||
alpha_factor_dst == LinearBlendingFactor::ConstantColor ||
alpha_factor_dst == LinearBlendingFactor::OneMinusConstantColor ||
color_factor_src == LinearBlendingFactor::ConstantAlpha ||
color_factor_src == LinearBlendingFactor::OneMinusConstantAlpha ||
color_factor_dst == LinearBlendingFactor::ConstantAlpha ||
color_factor_dst == LinearBlendingFactor::OneMinusConstantAlpha ||
alpha_factor_src == LinearBlendingFactor::ConstantAlpha ||
alpha_factor_src == LinearBlendingFactor::OneMinusConstantAlpha ||
alpha_factor_dst == LinearBlendingFactor::ConstantAlpha ||
alpha_factor_dst == LinearBlendingFactor::OneMinusConstantAlpha
{
if ctxt.state.blend_color != blend.constant_value {
let (r, g, b, a) = blend.constant_value;
unsafe { ctxt.gl.BlendColor(r, g, b, a); }
ctxt.state.blend_color = blend.constant_value;
}
}
let color_factor_src = color_factor_src.to_glenum();
let color_factor_dst = color_factor_dst.to_glenum();
let alpha_factor_src = alpha_factor_src.to_glenum();
let alpha_factor_dst = alpha_factor_dst.to_glenum();
if ctxt.state.blend_func != (color_factor_src, color_factor_dst,
alpha_factor_src, alpha_factor_dst)
{
unsafe {
ctxt.gl.BlendFuncSeparate(color_factor_src, color_factor_dst,
alpha_factor_src, alpha_factor_dst);
}
ctxt.state.blend_func = (color_factor_src, color_factor_dst,
alpha_factor_src, alpha_factor_dst);
}
}
Ok(())
}