I have a massive UIImage downloaded from a web resource (>300MBs) which when I attempt to render is causing an app crash due to memory. I am trying to resize the image using the following code:
+ (UIImage *)imageWithImage:(UIImage *)image scaled:(float) scale {
//UIGraphicsBeginImageContext(newSize);
CGSize size = (CGSize){scale * image.size.width, scale * image.size.height};
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
The problem is, as you may have guessed, that this requires actually rendering the image and thus causes the same crash. Is there a way to resize an large image without placing such an enormous memory strain on the system?
The problem is that you are holding the image in memory. Download it to disk and use the ImageIO framework to read a "thumbnail" (smaller size) directly from disk without ever having to hold the full-size image in memory.
You can try this one + (UIImage *)decodedImageWithImage:(UIImage *)image maxSize:(NSInteger) maxSize.
Code from SDWebImage
#define MAX_IMAGE_SIZE 1000
@implementation UIImage (ForceDecode)
+ (CGSize) originalSize:(UIImage*) image
{
return CGSizeMake(CGImageGetWidth(image.CGImage), CGImageGetHeight(image.CGImage));
}
+ (UIImage *)decodedImageWithImage:(UIImage *)image {
return [self decodedImageWithImage:image maxSize:MAX_IMAGE_SIZE];
}
+ (UIImage *)decodedImageWithImage:(UIImage *)image maxSize:(NSInteger) maxSize{
if (image.images) {
// Do not decode animated images
return image;
}
CGImageRef imageRef = image.CGImage;
CGSize imageSize = CGSizeMake(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef));
imageSize = [self checkMaxImageSize:imageSize maxSize:maxSize];
CGRect imageRect = (CGRect){.origin = CGPointZero, .size = imageSize};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
int infoMask = (bitmapInfo & kCGBitmapAlphaInfoMask);
BOOL anyNonAlpha = (infoMask == kCGImageAlphaNone ||
infoMask == kCGImageAlphaNoneSkipFirst ||
infoMask == kCGImageAlphaNoneSkipLast);
// CGBitmapContextCreate doesn't support kCGImageAlphaNone with RGB.
// https://developer.apple.com/library/mac/#qa/qa1037/_index.html
if (infoMask == kCGImageAlphaNone && CGColorSpaceGetNumberOfComponents(colorSpace) > 1) {
// Unset the old alpha info.
bitmapInfo &= ~kCGBitmapAlphaInfoMask;
// Set noneSkipFirst.
bitmapInfo |= kCGImageAlphaNoneSkipFirst;
}
// Some PNGs tell us they have alpha but only 3 components. Odd.
else if (!anyNonAlpha && CGColorSpaceGetNumberOfComponents(colorSpace) == 3) {
// Unset the old alpha info.
bitmapInfo &= ~kCGBitmapAlphaInfoMask;
bitmapInfo |= kCGImageAlphaPremultipliedFirst;
}
// It calculates the bytes-per-row based on the bitsPerComponent and width arguments.
CGContextRef context = CGBitmapContextCreate(NULL,
imageSize.width,
imageSize.height,
CGImageGetBitsPerComponent(imageRef),
0,
colorSpace,
bitmapInfo);
CGColorSpaceRelease(colorSpace);
// If failed, return undecompressed image
if (!context) return image;
CGContextDrawImage(context, imageRect, imageRef);
CGImageRef decompressedImageRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
UIImage *decompressedImage = [UIImage imageWithCGImage:decompressedImageRef scale:image.scale orientation:image.imageOrientation];
CGImageRelease(decompressedImageRef);
return decompressedImage;
}
+ (CGSize) checkMaxImageSize:(CGSize) size maxSize:(NSInteger) max
{
CGFloat whRatio = size.width / size.height;
if (size.height > max || size.width > max) {
if (size.height > size.width) {
size.width = max * whRatio;
size.height = max;
}else{
size.width = max ;
size.height = max / whRatio;
}
}
return size;
}