If you have ever worked with user generated images in an iOS app, you would know that it is very easy to quickly spend all the memory you have which would then result in your app being terminated by the OS. In order to support high resolution images in an app, I searched around for ways to resize images on device. The easiest and the most common way is to use CoreGraphics
to draw the image to a smaller rect and then convert it back to a UIImage
. The problem with this approach is that it isn’t gaining you anything because for drawing the image, you are decoding it anyway which requires reading the full sized image and might crash the app.
To correctly resize the image without decoding it first is to drop down a level and use the ImageIO
framework. The first thing you need with the ImageIO
framework is to create an image source from an image (the image can be an existing UIImage
, path to a file or even an ALAsset
). Let’s see how we can resize an image when we know the local path to the image. If you have the image in some other form, all you need to change is this step (see this for details).
The next step is to use the CGImageSourceCreateThumbnailAtIndex
method to actually generate the thumbnail image from this source. We need to pass some options here to configure the thumbnail generation (e.g. the max pixel size). kCGImageSourceCreateThumbnailWithTransform
option automatically rotates the image to the correct orientation and kCGImageSourceCreateThumbnailFromImageAlways
asks the OS to create the thumbnail even if it is already present. This option is important as without it, the OS might use an existing thumbnail which can be much smaller than what you asked for.
- (void)resizeImageAtPath:(NSString *)imagePath {
// Create the image source
CGImageSourceRef src = CGImageSourceCreateWithURL((__bridge CFURLRef) [NSURL fileURLWithPath:imagePath], NULL);
// Create thumbnail options
CFDictionaryRef options = (__bridge CFDictionaryRef) @{
(id) kCGImageSourceCreateThumbnailWithTransform : @YES,
(id) kCGImageSourceCreateThumbnailFromImageAlways : @YES,
(id) kCGImageSourceThumbnailMaxPixelSize : @(640)
};
// Generate the thumbnail
CGImageRef thumbnail = CGImageSourceCreateThumbnailAtIndex(src, 0, options);
CFRelease(src);
// Write the thumbnail at path
CGImageWriteToFile(thumbnail, imagePath);
}
Now that we have the CGImageRef
for the small image, let’s see how we can save the image back to the path. We will use the CGImageDestinationAddImage
function to write the image.
void CGImageWriteToFile(CGImageRef image, NSString *path) {
CFURLRef url = (__bridge CFURLRef) [NSURL fileURLWithPath:path];
CGImageDestinationRef destination = CGImageDestinationCreateWithURL(url, kUTTypePNG, 1, NULL);
CGImageDestinationAddImage(destination, image, nil);
if (!CGImageDestinationFinalize(destination)) {
NSLog(@"Failed to write image to %@", path);
}
}
This is all you need to load larger images in form of something that you can safely understand. We even tried this with a 6019×6019 image from NASA without any memory problems.