The Whizzo Blog

Preserving alpha when creating a UIImage from OpenGL ES

There's a lot of example code floating around out there demonstrating how to create a UIKit UIImage object from an OpenGL ES view. Most of the examples I've seen fail to take into consideration the alpha information coming from OpenGL.

I won't get into every nuance here for code clarity purposes, but the typical code looks something like this:

// bind to the correct framebuffer object
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);

// create buffer and read pixels into it
NSInteger length = width * height * 4;
GLubyte *buffer = (GLubyte*)malloc(length);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

// OpenGL inverts the y-axis vs. UIKit, so you need some code to correct it

// set up image creation parameters
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer, length, NULL);
int bitsPerComponent = 8;
int bitsPerPixel = 32;
int bytesPerRow = 4 * width;
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaLast;

// create the CGImage and then the UIImage
CGImageRef imageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, kCGRenderingIntentDefault);
UIImage *image = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);

Let's take a look at the bitmapInfo parameter. This provides the component information for the bitmap image being created.

The first mask, kCGBitmapByteOrderDefault, simply indicates to use default byte ordering.

The second mask, kCGImageAlphaLast, is the important one as far as transparency is concerned (and the one that seems to be excluded from most examples). It indicates whether the bitmap has an alpha channel or not. Assuming your OpenGL buffer has alpha information (you did set the pixel format correctly when you set it up?), Core Graphics will include the alpha information in the bitmap it generates.

This particular issue drove me nuts for a little while since I'm fairly new to OpenGL and spent all my time troubleshooting my buffer setup, my glClear call alpha, etc. It wasn't until I decided to confirm that UIKit was doing the right thing that I came across the solution. Hopefully this will help someone somewhere save a bit of time.

Posted on May 18, 2010 by Dan.