Kabaam is available

Holger Weissböck on September 11, 2011

Hi everyone,

Daniel has already teased this in the recent podcast about Sparrow ;-) but now it is official: Our fun little project Kabaam has hit the App Store today. Naturally it’s a single malt Sparrow App seasoned with just a little bit of UIKit.

Beam it to your iPhone

As if that wasn’t enough happiness for today, there is one more sparrow-related thing I’d like to share that came up during development:

How to convert SPRenderTextures to UIImages

One thing Kabaam does is overlay some text bubbles onto a photo the user shot. The photos/comics can then be exported to email and diverse social platforms. In order to accomplish that the whole display tree has to be rendered to an exportable image. Although we’d love to stay 100% pure Sparrow for most of the time ;-) it makes sense to use UIKit to help us here. That’s why I extended the SPRenderTexture to allow rendering it to an UIImage. Many of you most likely already know how to do this, but maybe this snippet still helps one or the other to get earlier results.

Here is the interface extension:

@interface SPRenderTexture (RenderToImage)
- (UIImage*)renderToImage;
@end

And here is the magical part. In short, it makes use of the bundleDrawCalls method on the SPRenderTexture to get to the current GL context. It then allocates memory for the resulting image and reads the pixels from the GL context into the allocated array. After that we can feed that array to a CGDataProvider, a CGImageRef and then to an UIImage. All that’s left to do is tidy up some memory, which is unfortuantely very C-ish in this case: We have to register a releaseRawImageDataBufferCallback with the CGDataProvider in order to release the allocated array when everything is over. Check it out:

@implementation SPRenderTexture (RenderToImage)

void releaseRawImageDataBufferCallback(void *info, const void *data, size_t size) 
{
	free((void*)data);
}

- (UIImage*)renderToImage
{
	__block UIImage *image = nil;
	
	[self bundleDrawCalls:^() {
		float scale = [SPStage contentScaleFactor];
		int width = scale * self.width;
		int height = scale * self.height;
		int nrOfColorComponents = 4; //RGBA
		int bitsPerColorComponent = 8;
		int rawImageDataLength = width * height * nrOfColorComponents;
		GLenum pixelDataFormat = GL_RGBA;
		GLenum pixelDataType = GL_UNSIGNED_BYTE;
		BOOL interpolateAndSmoothPixels = NO;
		CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
		CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;

		CGDataProviderRef dataProviderRef;
		CGColorSpaceRef colorSpaceRef;
		CGImageRef imageRef;
		
		@try {
			GLubyte *rawImageDataBuffer = (GLubyte *) malloc(rawImageDataLength);
			
			glReadPixels(0, 0, width, height, pixelDataFormat, pixelDataType, rawImageDataBuffer);
			
			dataProviderRef = CGDataProviderCreateWithData(NULL, rawImageDataBuffer, rawImageDataLength, releaseRawImageDataBufferCallback);
			colorSpaceRef = CGColorSpaceCreateDeviceRGB();
			imageRef = CGImageCreate(width, height, bitsPerColorComponent, bitsPerColorComponent * nrOfColorComponents, width * nrOfColorComponents, colorSpaceRef, bitmapInfo, dataProviderRef, NULL, interpolateAndSmoothPixels, renderingIntent);
			image = [UIImage imageWithCGImage:imageRef];
		}
		@finally {
			CGDataProviderRelease(dataProviderRef);
			CGColorSpaceRelease(colorSpaceRef);
			CGImageRelease(imageRef);
		}
	}];
	
	return image;
}

@end

As soon as we have that UIImage in our hands we can (thanks to UIKit convenience) very easily convert this into JPG or PNG format using the UIImageJPEGRepresentation and UIImagePNGRepresentation helpers.