After a bit of research, I think I now understand how Codea composites and why I get ‘alpha squared’ in the second example in my comment above - thanks to the Codea Runtime Library and the OpenGL ES 2.0 reference. If a more experienced user of Codea/OpenGL ES could check that I am on the right lines, I would be grateful.
As a shorthand, I’ll use ‘x’ and ‘y’ to represent a colour (in straight or pre-multiplied format),
where ‘x’ is the red, green and blue channels and ‘y’ is the alpha channel. I’m also still using the range 0 to 1 rather than the range 0 to 255. So, for a
straight format source colour (x, y) = (Sc, Sa) and for a pre-multiplied format source
colour (x, y) = (Sca, Sa), where Sca = Sc * Sa.
In each case, the destination (canvas) colour and the output of blending are in a pre-multiplied format - that is, (Dca, Da) and (Dca’, Da’).
If the source colour is in a pre-multiplied format, then the blending function for <a href=“http://en.wikipedia.org/wiki/Alpha_compositing”‘over’ alpha compositing is:
Dca' = 1 * x + (1 - Sa) * Dca
Da' = 1 * y + (1 - Sa) * Da
If the source colour is in a straight format, then the equivalent blending
function is:
Dca' = Sa * x + (1 - Sa) * Dca
Da' = 1 * y + (1 - Sa) * Da
In the Codea Runtime Library, the implementation of the sprite()
function sets
a blend mode of BLEND_MODE_PREMULT if the source image’s premultiplied flag is set and
a blend mode of BLEND_MODE_NORMAL otherwise.
In the case of BLEND_MODE_PREMULT, this results in a call to OpenGL:
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)
That is consistent with the blending function above: GL_ONE corresponds to the
factor 1 and GL_ONE_MINUS_SRC_ALPHA corresponds to the factor (1 - Sa).
In the case of BLEND_MODE_NORMAL, this results in a call to OpenGL:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
That is not consistent with the blending function above: GL_SRC_ALPHA
corresponds to the factor Sa, but for Da’ the factor required is 1 (not Sa). Using the factor Sa results in:
Dca' = Sa * x + (1 - Sa) * Dca
Da' = Sa * y + (1 - Sa) * Da
In cases where (Dca, Da) = (0, 0), this results in the output (Sca, Sa * Sa) - with the ‘alpha squared’ that I had noted.
What I do not understand is why the following call is not made in the case of
BLEND_MODE_NORMAL:
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA)
OpenGL’s glBlendFuncSeparate
allows different (separate) factors to be applied to the red, green and blue channels than those applied to the alpha channels.