Search code examples
ioscocos2d-iphonecore-graphicscgcontext

Why am I getting a black line around my 32-bit context in Cocos2D?


I'm having an issue with rendering my own sprites in Cocos2D. If I create a context, even with supposedly a premultiplied alpha, I get black artefacts. The following code should produce a completely white circle:

- (void)renderTest
{
    CGRect rect = CGRectMake(0, 0.0, 100.0, 100.0);

    unsigned char *data = calloc((int)rect.size.width * (int)rect.size.height, 4);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(data, (int)rect.size.width, (int)rect.size.height, 8, (int)rect.size.width*4, colorSpace, kCGImageAlphaPremultipliedLast|kCGBitmapByteOrder32Big);
    CGColorSpaceRelease(colorSpace);

    CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
    CGContextAddArc(context, rect.size.width / 2, rect.size.height / 2, rect.size.width * 0.4, 0.0, 2 * M_PI, 0);
    CGContextFillPath(context);

    CCTexture2D *tex = [[CCTexture2D alloc] initWithData:data pixelFormat:kCCTexture2DPixelFormat_RGBA8888 pixelsWide:rect.size.width pixelsHigh:rect.size.height contentSize:rect.size];

    self.texture = tex;
    self.textureRect = rect;

    free(data);
    CGContextRelease(context);
}

When placed on a white background, this should be invisible. However, in reality I get this:

enter image description here

If I do exactly the same thing in UIKit using UIGraphicsGetCurrentContext() then it works flawlessly without the black hairline.

Can anyone please tell me how to get it to render the alpha channel without dodgy artefacts?


Solution

  • It looks like rendering is done with a glBlendFunc that is not suitable for premultiplied colors.

    There are two (and a half) options to fix this:

    1. Set the blend function to glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA). I have no idea about this Cocos2D thing and if you can influence the blend function or tell it otherwise to use premultiplied colors.

    2. Don't use premultiplied colors in your bitmap context. I'm not sure if the resulting pixel format is supported on iOS, though.

    3. If everything is white you could just set the RGB parts of the data to 0xFFFFFF and just leave the alpha in place before creating the texture.

    Edit: Here's code that paint's everything in your texture white:

    uint32_t *ptr = (uint32_t *)data;
    void *end = data + CGBitmapContextGetBytesPerRow(context) * CGBitmapContextGetHeight(context);
    while (ptr != end)
        *ptr++ |= 0xffffff; // or 0xffffff00, depending on endianess and pixel format in context