How do I resize a large image on an iPhone 3G without crashing my app?
I am working on an app that downloads images from a web site for display on iOS devices. The app runs fine on an iPad, but when run on an iPhone 3G (the only other physical device at my disposal for testing), it crashes when loading large images. As suggested elsewhere, it seems that 1024 x 1024 is about the largest size image it can consistently handle.
To attempt to workaround this, I've added code to resize images larger than 1024 wide or 1024 high (code taken from here):
if (image.size.width > 1024 || image.size.height > 1024) {
// resize the image
float actualHeight = image.size.height;
float actualWidth = image.size.width;
float imgRatio = actualWidth/actualHeight;
float maxRatio = self.frame.size.width/self.frame.size.height;
if(imgRatio!=maxRatio) {
if(imgRatio < maxRatio) {
imgRatio = self.frame.size.height / actualHeight;
actualWidth = imgRatio * actualWidth;
actualHeight = self.frame.size.height;
} else {
imgRatio = self.frame.size.width / actualWidth;
actualHeight = imgRatio * actualHeight;
actualWidth = self.frame.size.width;
}
}
CGRect rect = CGRectMake(0.0, 0.0, actualWidth, actualHeight);
UIGraphicsBeginImageContext(rect.size);
[image drawInRect:rect];
imageToDraw = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
However, resizing very large images (for example, one that is about 3500 x 2500 pixels) still crashes the app.
Is there any other mechanism that I can use to safely resize such large images, or other mechanisms that I can use for display开发者_如何学编程ing them without resizing? I don't have the ability to tile the images outside of my app for use in a CATiledLayer, as I don't control the source of the images.
Thanks!
Edit: after some more debugging, I found the source of the crash (see answer below). However, I'd still welcome any advice regarding better ways to allow viewing of very large images on an iOS device without resizing the images.
I just noticed that the picture in question had the same aspect ratio as the screen (which was in landscape orientation when the crash occurred). Since imgRatio == maxRatio
for this image in the above code, no resizing occurred.
I modified the above code to look like this:
if (image.size.width > 1024 || image.size.height > 1024) {
// resize the image
float actualHeight = image.size.height;
float actualWidth = image.size.width;
float imgRatio = actualWidth/actualHeight;
float maxRatio = self.frame.size.width/self.frame.size.height;
if(imgRatio < maxRatio){
imgRatio = self.frame.size.height / actualHeight;
actualWidth = imgRatio * actualWidth;
actualHeight = self.frame.size.height;
}
else{
imgRatio = self.frame.size.width / actualWidth;
actualHeight = imgRatio * actualHeight;
actualWidth = self.frame.size.width;
}
CGRect rect = CGRectMake(0.0, 0.0, actualWidth, actualHeight);
UIGraphicsBeginImageContext(rect.size);
[image drawInRect:rect];
imageToDraw = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
Now it works for the image that was previously crashing the iPhone.
The other answer works well for small images, but when you try to resize a very large image, you will quickly run out of memory and crash the app. A much better way is to use CGImageSourceCreateThumbnailAtIndex
to resize the image without completely decoding it first.
If you have the path to the image you want to resize, you can use this:
- (void)resizeImageAtPath:(NSString *)imagePath {
// Create the image source (from path)
CGImageSourceRef src = CGImageSourceCreateWithURL((__bridge CFURLRef) [NSURL fileURLWithPath:imagePath], NULL);
// To create image source from UIImage, use this
// NSData* pngData = UIImagePNGRepresentation(image);
// CGImageSourceRef src = CGImageSourceCreateWithData((CFDataRef)pngData, 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);
}
More details here.
精彩评论