Cocoa in the Shell

UIImage scaling using ImageIO

In this post I explain what is the best way to create scaled images for iOS and Mac OS applications. By best way, I mean the most efficient one, because it’s a common operation to manipulate images, and so it should not penalize your application too much.

To illustrate this post, I created a simple iPad application which displays a static wall of images like on the picture below.

All the displayed pictures have an original size of 1024x768, and final size of 200x150.

This view is pretty simple, there are 16 UIImageView as subview, and a big UIImageView to display the selected image in fullscreen.

To display these images, I used 2 different methods which I’ll detail.
With these 2 methods I did benchmarks using two Instruments :

  • Time Profiler, to see how much time I was spending in the buildGallery method.

  • Allocations, to see how much memory was used.

First method, using UIKit

The first approach is to use UIKit, to do this I used MGImageUtilities from Matt Gemmell, which offers the method imageScaledToFitSize:.

My buildGallery method looked like this :

-(void)buildGallery
{
    for (NSUInteger i = 0; i < kMaxPictures; i++)
    {
        NSInteger imgTag = i + 1;
        NYXPictureView* v = [[NYXPictureView alloc] initWithFrame:(CGRect){.origin.x = x, .origin.y = y, .size = _thumbSize}];
        NSString* imgPath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"%d", imgTag] ofType:@"jpg"];
        UIImage* fullImage = [[UIImage alloc] initWithContentsOfFile:imgPath];
        [v setImage:[fullImage imageScaledToFitSize:_thumbSize]];
        [fullImage release];
    }
}

Results of the benches gave me the following :

  • Time Profiler : 4233ms

  • Live bytes : 695Kb

  • Overall bytes used : 78.96Mb

Second method, using ImageIO

The second method uses the ImageIO framework, which is available since iOS 4, so we need to link with ImageIO.framework.
The interesting feature of this framework is CGImageSource which allows us to manipulate a great variety of image format, and to create thumbnails from them.

Now the method looks like this :

-(void)buildGallery
{
    for (NSUInteger i = 0; i < kMaxPictures; i++)
    {
        NSInteger imgTag = i + 1;
        NYXPictureView* v = [[NYXPictureView alloc] initWithFrame:(CGRect){.origin.x = x, .origin.y = y, .size = _thumbSize}];
        NSString* imgPath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"%d", imgTag] ofType:@"jpg"];
        CGImageSourceRef src = CGImageSourceCreateWithURL((CFURLRef)[NSURL fileURLWithPath:imgPath], NULL);
        CFDictionaryRef options = (CFDictionaryRef)[[NSDictionary alloc] initWithObjectsAndKeys:(id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailWithTransform, (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageIfAbsent, (id)[NSNumber numberWithDouble:_maxSize], (id)kCGImageSourceThumbnailMaxPixelSize, nil];
        CGImageRef thumbnail = CGImageSourceCreateThumbnailAtIndex(src, 0, options); // Create scaled image
        CFRelease(options);
        CFRelease(src);
        UIImage* img = [[UIImage alloc] initWithCGImage:thumbnail];
        [v setImage:img];
        [img release];
        CGImageRelease(thumbnail);
    }
}

The benches gave me this :

  • Time Profiler : 3433ms

  • Live bytes : 681Kb

  • Overall bytes used : 77.63Mb

Conclusion

You can see that using ImageIO is about 19% faster than UIKit, and also uses slightly less memory.

So, my advise is to use ImageIO to perform images operations because it’s more efficient and it just requires a bit more code, but you can write your own tiny class to reuse in all your projects.