I was working on NetSketch last night and ran into an interesting problem. To optimize the display of thumbnail images, I decided to cache them at their display size and not at full size (320×380). It worked out really well and led to a huge performance boost at launch. The allocation of memory is so slow on the iPhone that leaving around a bunch of large images just isn’t an option – especially when they all need to be loaded at the same time.

I wrote a convenience function to do the heavy lifting, and I’ve posted the source below. Given a CGImageRef and a desired scale (1.0 being the same), it returns another CGImage of a different scale. Of course, for many scenarios you can just shove the full size image into a UIImage and let UIKit resize the image for you. That approach doesn’t perform anti-aliasing, though – and will give you a slightly more grainy result.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
 
CGImageRef CreateScaledCGImageFromCGImage(CGImageRef image, float scale)
{
// Create the bitmap context
CGContextRef    context = NULL;
void *          bitmapData;
int             bitmapByteCount;
int             bitmapBytesPerRow;
 
// Get image width, height. We'll use the entire image.
int width = CGImageGetWidth(image) * scale;
int height = CGImageGetHeight(image) * scale;
 
// Declare the number of bytes per row. Each pixel in the bitmap in this
// example is represented by 4 bytes; 8 bits each of red, green, blue, and
// alpha.
bitmapBytesPerRow   = (width * 4);
bitmapByteCount     = (bitmapBytesPerRow * height);
 
// Allocate memory for image data. This is the destination in memory
// where any drawing to the bitmap context will be rendered.
bitmapData = malloc( bitmapByteCount );
if (bitmapData == NULL)
{
return nil;
}
 
// Create the bitmap context. We want pre-multiplied ARGB, 8-bits
// per component. Regardless of what the source image format is
// (CMYK, Grayscale, and so on) it will be converted over to the format
// specified here by CGBitmapContextCreate.
CGColorSpaceRef colorspace = CGImageGetColorSpace(image);
context = CGBitmapContextCreate (bitmapData,width,height,8,bitmapBytesPerRow,
colorspace,kCGImageAlphaNoneSkipFirst);
CGColorSpaceRelease(colorspace);
 
if (context == NULL)
// error creating context
return nil;
 
// Draw the image to the bitmap context. Once we draw, the memory
// allocated for the context for rendering will then contain the
// raw image data in the specified color space.
CGContextDrawImage(context, CGRectMake(0,0,width, height), image);
 
CGImageRef imgRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
free(bitmapData);
 
return imgRef;
}