Corruption in OpenGL ES Textures with Alpha on the iPhone
In the progress of working on my little iPhone game (really more of a technology demonstrator), I discovered strange artifacts in the transparent areas of a texture:

The area around the red X should be transparent, but you will notice odd discolorations, almost reminiscent of jpeg artifacts.
The problem, I discovered, lay in how I was loading the texture:
CGImageRef spriteImage; GLubyte *spriteData; CGContextRef spriteContext; GLuint spriteTexture; GLenum err; size_t width, height; // load the image into a sprite spriteImage = [UIImage imageNamed:resourceName].CGImage; width = CGImageGetWidth(spriteImage); height = CGImageGetHeight(spriteImage); // render onto a context spriteData = (GLubyte*)malloc(width * height * 4); spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width * 4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast); CGContextDrawImage(spriteContext, CGRectMake(0.0,0.0,(CGFloat)width,(CGFloat)height), spriteImage); CGContextRelease(spriteContext); // generate texture glGenTextures(1, &spriteTexture); glBindTexture(GL_TEXTURE_2D, spriteTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData); free(spriteData);
In short, the above code renders the texture from the CGImage of the compiled resource to a bitmap in the memory pointed to by the spriteData pointer. OpenGL then reads in the texture from that bitmap into its own store.
The problem is that for some reason when the CGContextDrawImage() function renders the image to the bitmap, it does not render the transparent pixels. This actually makes sense when you think about it, since you can't render things that aren't there.
The solution then, is simple. We must zero out the bytes in the bitmap before rendering to it, either by calling memset(spriteData, 0, width * height * 4); or replacing the call to malloc() with a call to calloc(). It would also be sufficient to only set every 4th byte to 0, since that is the alpha byte, but that solution, as well as a justification, is left as an exercise to the reader.
Thank you, thank you, thank you!
I was suffering from this problem for a while and had no idea why it was happening. Zeroing out the malloc'd memory worked.
Incidentally, it would seem that the iPhone simulator does not zero out newly malloc'd memory but the iPhone itself does. At least, that is my experience - I might be mistake.
Thanks again.
Thanks for the fix! I assumed that CGBitmapContextCreate() would set every byte in the buffer, so it never occurred to me that I'd need to zero out the buffer first. But I suppose that's a good practice anyway.
It's strange that the simulator needs this fix, but the actual iPhone seems to do things properly without it.
It seems that zeroing out all data causes transparent areas to be too dark, perhaps because the iPhone uses premultiplied alpha.
This is what I'm using now:
memset32( spriteData, 0xFFFFFF00, width * height * 4 );
void memset32( void *ptr, unsigned int value, size_t size )
{
for( size_t i = 0; i < size; i ++ )
*( ((unsigned char *) ptr) + i ) = ( value >> (3 - i%4) ) % 0x100;
}
I know this is a little late after the fact, but you might consider using calloc, rather than malloc, as that function guarantees to return zeroed memory (or NULL). Obviously, it's not what you want if you're after transparent white, as raptor007 wants.
Add a Comment