utils/drawing/drawing_color.rb
module DrawingColor
def current_color_array
GL.GetDoublev(GL::CURRENT_COLOR)
end
def current_color
Color.new(current_color_array)
end
def with_color(color)
return yield unless color
saved = GL.GetColorArray
c = color.to_a
GL.Color([c[0], c[1], c[2], (c[3] || 1.0) * (saved[3] || 1.0)]) # NOTE: doesn't set alpha-- instead multiplies it
yield
GL.Color(*saved)
end
def with_color_listsafe(color)
GL.PushAttrib(GL_CURRENT_BIT)
c = color.to_a
GL.Color4f(c[0], c[1], c[2], c[3] || 1.0) # NOTE: doesn't set alpha-- instead multiplies it
yield
GL.PopAttrib
end
def with_color_and_alpha(color)
saved = GL.GetColorArray
GL.Color(*color.to_a)
yield
GL.Color(*saved)
end
def with_alpha(alpha)
saved = GL.GetColorArray
GL.Color([saved[0], saved[1], saved[2], alpha])
yield
GL.Color(*saved)
end
def with_multiplied_alpha(multiplier)
return if multiplier == 0.0
return yield if multiplier == 1.0
saved = GL.GetDoublev(GL::CURRENT_COLOR)
c = saved.dup # TODO: can we avoid this?
c[3] *= multiplier
GL.Color(*c)
yield
GL.Color(*saved)
end
def with_gl_blend_function(a, b)
# TODO: get/set old mode
GL.BlendFunc(a, b)
yield
GL.BlendFunc(GL::SRC_ALPHA, GL::ONE_MINUS_SRC_ALPHA)
end
def with_gl_color_logic_op(op)
# TODO: get/set old mode
GL.Enable(GL::COLOR_LOGIC_OP)
GL.LogicOp(op)
yield
GL.Disable(GL::COLOR_LOGIC_OP)
end
def with_gl_blend_equation(mode)
# TODO: get/set old mode
GL.BlendEquationEXT(mode)
yield
GL.BlendEquationEXT(GL::FUNC_ADD_EXT)
end
DRAW_METHOD_OPTIONS = [[:average, 'Average'], [:brighten, 'Brighten'], [:darken, 'Darken'], [:multiply, 'Multiply'], [:invert, 'Invert'], [:min, 'Min'], [:max, 'Max']]
def with_pixel_combine_function(name)
return yield unless name
case name
#
# Average, the default mode
# Set ((source * alpha) + (dest * (1 - alpha))) as destination
#
when :average
with_gl_blend_function(GL::SRC_ALPHA, GL::ONE_MINUS_SRC_ALPHA) {
with_gl_blend_equation(GL::FUNC_ADD_EXT) {
yield
}
}
#
# Brighten ("Screen" in Photoshop terms)
# Add (source * alpha) to destination pixel
#
when :brighten
with_gl_blend_equation(GL::FUNC_ADD_EXT) {
with_gl_blend_function(GL::SRC_ALPHA, GL::ONE) {
yield
}
}
#
# Darken
# Subtract (source * alpha) from destination pixel
#
when :darken
with_gl_blend_function(GL::SRC_ALPHA, GL::ONE) {
with_gl_blend_equation(GL::FUNC_REVERSE_SUBTRACT_EXT) { # (Cd*df - Cs*sf)
yield
}
}
#
# Multiply
# Set (source * destination) as destination pixel (NOTE: ignores alpha)
#
when :multiply
with_gl_blend_function(GL::DST_COLOR, GL::ZERO) {
with_gl_blend_equation(GL::FUNC_ADD_EXT) {
yield
}
}
#
# Invert
# Set (source ^ destination) as destination color (NOTE: ignores alpha)
#
# NOTE: XOR is like INVERT when source is white: (WHITE ^ destination) == (!destination)
# but, unlike INVERT, respects an alternate source color when set
#
when :invert then with_gl_color_logic_op(GL::XOR) { yield }
#
# Min
# Sets min(source, destination) as destination (for each RGB component)
#
when :min then with_gl_blend_equation(GL::MIN_EXT) { yield }
#
# Max
# Sets max(source, destination) as destination (for each RGB component)
#
when :max then with_gl_blend_equation(GL::MAX_EXT) { yield }
else # do nothing
yield
end
end
#
# Alpha Test
#
def with_alpha_test(alpha_cutoff)
if GL.IsEnabled(GL::ALPHA_TEST)
GL.AlphaFunc(GL::GREATER, alpha_cutoff)
yield
else
GL.Enable(GL::ALPHA_TEST)
GL.AlphaFunc(GL::GREATER, alpha_cutoff)
yield
GL.Disable(GL::ALPHA_TEST)
end
end
end