2

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 displaying 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.

Community
  • 1
  • 1
Greg
  • 33,450
  • 15
  • 93
  • 100

2 Answers2

1

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.

Greg
  • 33,450
  • 15
  • 93
  • 100
  • it will be awesome if you will write the whole method and not just a part of it. thanks – Dmitry Oct 06 '10 at 14:39
  • What more are you looking for? This is all the code that pertains to the image resizing, which was the problematic code. The other code in the method is not related. – Greg Oct 06 '10 at 15:49
0

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 CGImageSourceCreateThumbnailAtIndexto 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.

Community
  • 1
  • 1
Pulkit Goyal
  • 5,604
  • 1
  • 32
  • 50