Currently I am spending some (if you ask my wife, she would probably say: a lot) of my free time writing a little app for the iPhone as a native frontend to one of our products, a deliverable management system (conjectPM).
In 1990 I got myself a NeXTStation and started to play around with NeXTStep and Objective-C. I really loved the AppKit as it was called back then. I became an iPhone developer as soon as Apple created the program and immediately got eh iPhone SDK. I have to say, programming the iPhone is as much a joy as working on NeXTStep was back then. The fundamentals haven’t changed much. The frameworks still are based on a very clear MVC pattern with lots of delegation to separate framework functionality from your own code. XCode is a nice environment but by far not as evolved as IntelliJ IDEA. A huge amount of often needed functionality is just there (like splitting a NSString containing a path into it’s components), but not having garbage collection and the arcane [object message:param]; syntax is quite cumbersome.
Because I want to mark the icons of favorites with a blue star and I don’t want to create every icon twice, I wanted to just superimpose the star onto the standard icon for the document type. It wasn’t as easy as
UIImage *result = [originalIcon compositeWith:starIcon];
Here is how to do it (based on the example in SDK documentation and helped by a hint from SkylarEC)
-
- (UIImage *) getIconOfSize:(CGSize) size {
-
UIImage *icon = [[UIImage imageNamed:self.iconName] scaleImageToSize:size];
-
UIImage *overlay = [UIImage imageNamed:@"BlueStar.png"];
-
CGRect iconBoundingBox = CGRectMake (0, 0, size.width, size.height);
-
CGRect overlayBoundingBox = CGRectMake (size.width/2, size.height/2,
-
size.width/2, size.height/2);
-
CGContextRef myBitmapContext = [self createBitmapContextOfSize:size];
-
CGContextSetRGBFillColor (myBitmapContext, 1, 1, 1, 1);
-
CGContextFillRect (myBitmapContext, iconBoundingBox);
-
CGContextDrawImage(myBitmapContext, iconBoundingBox, icon.CGImage);
-
CGContextDrawImage(myBitmapContext, overlayBoundingBox, overlay.CGImage);
-
UIImage *result = [UIImage imageWithCGImage: CGBitmapContextCreateImage (myBitmapContext)];
-
CGContextRelease (myBitmapContext);
-
return result;
-
}
- Create the CGContextRef
- draw both icons with the correct bounding rectangles into the CGContext
- Get the combined UIImage out of the CGContext with CGBitmapContextCreateImage
- done
Here is how to create the CGContextRef (based on the SDK example)
-
- (CGContextRef) createBitmapContextOfSize:(CGSize) size {
-
CGContextRef context = NULL;
-
CGColorSpaceRef colorSpace;
-
void * bitmapData;
-
int bitmapByteCount;
-
int bitmapBytesPerRow;
-
-
bitmapBytesPerRow = (size.width * 4);
-
bitmapByteCount = (bitmapBytesPerRow * size.height);
-
-
colorSpace = CGColorSpaceCreateDeviceRGB();
-
if (bitmapData == NULL) {
-
return NULL;
-
}
-
context = CGBitmapContextCreate (bitmapData,
-
size.width,
-
size.height,
-
8, // bits per component
-
bitmapBytesPerRow,
-
colorSpace,
-
kCGImageAlphaPremultipliedLast);
-
CGContextSetAllowsAntialiasing (context,NO);
-
if (context== NULL) {
-
return NULL;
-
}
-
CGColorSpaceRelease( colorSpace );
-
return context;
-
}
Beware: In the original example the colorSpace on line 11 is created like this
colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
This is deprecated. Use GColorSpaceCreateDeviceRGB() instead.




Wow! First hit in Google and this is EXACTLY what I need to do for one of my projects. Thanks SO much!!
Sure thing – glad to help
I got a white background instead of a clear overlay.
Remove the white background:
CGContextSetRGBFillColor (myBitmapContext, 1, 1, 1, 1);
CGContextFillRect (myBitmapContext, iconBoundingBox);
And instead, start with a blank slate:
CGContextClearRect (myBitmapContext, iconBoundingBox);
That will preserve any alpha in your icons.
Thanks, Joe.
I noticed that problem originally as well.
As my icons are rendered on a white background it wasn’t a problem.
Preserving alpha is, of course, better.
Joe thanks for the example!
Thank you kindly for this snippet of code! I’m relatively new to developing on the iPhone SDK with Objective-C, and while I don’t FULLY understand what’s going on in the createBitMapContextOfSize:size function, it’s pointing me in the proper direction. Thank you for sharing!
Tried to reuse your code. Isn’t there a memory leak due to
bitmapData = malloc( bitmapByteCount );
never being freed when context is not NULL?
@chrizz, you are right.
Starting with iOS 4.0 and on OS X 10.6 you can pass NULL if you want Quartz to allocate memory for the bitmap. According to the docs “This frees you from managing your own memory, which reduces memory leak issues.”
So this becomes
colorSpace = CGColorSpaceCreateDeviceRGB();
context = CGBitmapContextCreate (NULL,
size.width,
size.height,
8, // bits per component
bitmapBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedLast);
CGContextSetAllowsAntialiasing (context,NO);
No more bitmapData, passing NULL instead to CGBitmapContextCreate()
Fantastic – just what I needed.