Alpha Blending not working as expected

I am rendering a sprite through CCRenderTexture and alpha blending does not seem work as expected. The pixels with low alpha get darkened. See the attached image. On left the sprite is rendered as-is. On the right side I am creating the same sprite through CCRenderTexture.

The code is like this:

// INITALIZE ORIGINAL SPRITE

CCSprite* original = CCSprite::create("Test/Sprite.png");
addChild(original);

// INITALIZE RENDERED SPRITE

CCSprite* rendered = renderedSprite(original);
addChild(rendered);

/**
 * Should render a CCSprite as-is into a CCRenderTexture and return that as a new CCSprite.
 */ 
CCSprite* TestLayer::renderedSprite(CCSprite* text) {
    CCRenderTexture* rt = CCRenderTexture::create(text->getContentSize().width, text->getContentSize().height);
    text->setPosition(ccp(text->getContentSize().width / 2, text->getContentSize().height / 2));

    rt->beginWithClear(0.0f, 0.0f, 0.0f, 0.0f);
    text->visit();
    rt->end();

    CCSprite* result = CCSprite::createWithTexture(rt->getSprite()->getTexture());
    return result;
}


Sprite.png (10.5 KB)


cocos2d.png (37.8 KB)

It looks like the sprite data are being pre-multiplied twice, though I can’t find where that happens.
Interestingly if I change the last lines:

    CCSprite* result = CCSprite::createWithTexture(rt->getSprite()->getTexture());
    return result;

with these:

    // Return the CCRenderTexture internal CCSprite.
    rt->getSprite()->setParent(0);
    return rt->getSprite();

The rendering is correct. So I thought createWithTexture() is causing this, but it is not because if I also change the lines above with these:

// Notice, I bypass completely CCRenderTexture just to test createWithTexture().
CCSprite* result = CCSprite::createWithTexture(text->getTexture());
return result;

Also renders correctly.

Alright, I found what was wrong. I post in case others hit the same issue.

  1. Upon Sprite creation with an image, cocos pre-multiplies the image data and sets the blending mode to (GL_ONE, GL_ONE_MINUS_SRC_ALPHA). <— OK
  2. Upon RenderTexture creation:
    1. An internal Sprite is also created with blending mode (GL_ONE, GL_ONE_MINUS_SRC_ALPHA). <— OK
    2. An internal Texture2D is created but with hasPremultipliedAlpha == false. <— OOPS

At this point the outer Sprite, as well as the RenderTexture internal Sprite, render correctly because they have the correct blending mode.

Creating the final Sprite with the RenderTexture Texture2D, sets wrongly the blending mode to (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA), but the data has been pre-multiplied (point 1). Now when OpenGL renders the Sprite data, multiplies them again…

The solution is to correct the blending mode:

CCSprite* result = CCSprite::createWithTexture(rt->getSprite()->getTexture());
result->setBlendFunc((ccBlendFunc) { GL_ONE, GL_ONE_MINUS_SRC_ALPHA }); // <-- CORRECT BLENDING MODE
return result;
1 Like

Thank you for posting your workaround/fix.

The exact same problem still exists at this very moment in v3.3. Could you guys please fix it? It’s definetly something people will continue to stumble upon.

hasPremultipliedAlpha = false, should be true as OP described.

It’s very helpful, thanks a lot.