CGBitmapContextCreate on the iPhone/iPad

toastie picture toastie · Apr 12, 2010 · Viewed 11.9k times · Source

I have a method that needs to parse through a bunch of large PNG images pixel by pixel (the PNGs are 600x600 pixels each). It seems to work great on the Simulator, but on the device (iPad), i get an EXC_BAD_ACCESS in some internal memory copying function. It seems the size is the culprit because if I try it on smaller images, everything seems to work. Here's the memory related meat of method below.

+ (CGRect) getAlphaBoundsForUImage: (UIImage*) image 
{    
    CGImageRef imageRef = [image CGImage];

NSUInteger width = CGImageGetWidth(imageRef);
NSUInteger height = CGImageGetHeight(imageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

unsigned char *rawData = malloc(height * width * 4);
memset(rawData,0,height * width * 4);

NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;
CGContextRef context = CGBitmapContextCreate(rawData, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

CGColorSpaceRelease(colorSpace);

CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
CGContextRelease(context);

/* non-memory related stuff */

free(rawData);

When I run this on a bunch of images, it runs 12 times and then craps out, while on the simulator it runs no problem. Do you guys have any ideas?

Answer

Duncan C picture Duncan C · Feb 26, 2012

Running 12 times and then crashing sounds like a running out of memory problem. It might be that internally the CGContext is creating some large autoreleased structures. Since you're doing this in a loop, they are not getting freed, so you run out of memory and die.

I'm not sure how Core Foundation deals with temporary objects though. I don't think CF objects have the equivalent of autorelease, and a Core Graphics context is almost certainly dealing with CF objects rather than NSObjects.

To reduce the memory churn in your code, I would suggest refactoring it to create an offscreen CGContext once before you start processing, and use it repeatedly to process each image. Then release it when you are done. That is going to be faster in any case (since you aren't allocating huge data structures on each pass through the loop.)

I'll wager that will eliminate your crash problem, and I bet it also makes your code much, much faster. Memory allocation is very slow compared to other operations, and you're slinging around some pretty big data structures to handle 600x600 pixel RGBA images.