0

I am working with image. I have a cropping functionality which only allows cropping in 2:1 ratio (width:height ratio). So if the image is in other ratio the user can zoom out and fit the image inside the cropping window but I want to add black area to the side of the image so that the resultant image is always in 2:1 ratio.

Please see the screenshot

enter image description here

This is the code I am using:

#Check the ratio
#Get the resultant size ie. resultant width and height

//crashes here
UIGraphicsBeginImageContextWithOptions(CGSizeMake(newImageWidth, newImageHeight), YES, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
UIGraphicsPushContext(context);
CGPoint origin = CGPointMake((newImageWidth - image.size.width) / 2.0f, (newImageHeight - image.size.height) / 2.0f);
[image drawAtPoint:origin];
UIGraphicsPopContext();

This code is working fine in iOS app but when I use this code in app extension it crashes because of memory leak. It crashes in UIGraphicsBeginImageContextWithOptions.

Is there any other way to add black area to the image without memory leak?

Hope you understand the problem.

Thanks in advance.

George
  • 3,600
  • 2
  • 26
  • 36

1 Answers1

1

UIImageView is very good at aspect fitting, filling, scaling, etc. operations. You can let one do the computational and bitblit work for you, then render the result with an image context. Untested, but like this...

// answer a new UIImage that has 2:1 aspect and fits a given input image
// for now, scale it clumsily and extra small to prove or rule out memory issue
+ (UIImage *)aspectFit:(UIImage *)image {
    CGRect frame = CGRectMake(0, 0, 200, 100);
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
    imageView.backgroundColor = [UIColor blackColor];
    imageView.contentMode = UIViewContentModeScaleAspectFit;
    imageView.image = image;

    UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, YES, 0.0);
    [imageView.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return result;
}

The scaling calculation can be made more sophisticated by setting a max value in either dimension and scaling to that. Choosing the value to limit to is a matter of the app needs (often, the current display size is helpful). The calculation is straight-forward...

// calculate a frame smaller than the starting image to scale to
CGFloat maxHeight = 600; // or an fn() of display, up to the app needs
CGSize size = image.size;
CGFloat scale = MAX(size.height, maxHeight) / size.height;
CGRect frame = CGRectMake(0, 0, size.width*scale, size.height*scale);
danh
  • 62,181
  • 10
  • 95
  • 136
  • 1
    In your answer you are still using UIGraphicsBeginImageContextWithOptions, this is where the app crashed. It is still crashing for images with large dimensions. – George Sep 17 '19 at 05:56
  • Please post the crash. What size do mean by “large”? You may need to down sample before doing anything with an overly large image. – danh Sep 17 '19 at 06:11
  • The extension simply crashes and I am not getting any log. The issue happens when I try to crop Live Photos. But crash is not happening for all Live Photos, just random Live Photos. I suspect it has to do with the size of the images. – George Sep 17 '19 at 07:02
  • How about down scaling? – danh Sep 17 '19 at 12:41
  • can you suggest a way for down scaling – George Sep 17 '19 at 13:02
  • @George - see edit, though I'm a little skeptical about the idea of image size (or a leak) being the issue. Live images sounds more suspicious. (Can you log image dimensions and post those?) – danh Sep 17 '19 at 13:09
  • image dimensions are width-3024 and height-4032. I tried the scaling down option and it worked fine. – George Sep 17 '19 at 13:16
  • Well, that is pretty big, but not crushingly so. The next testing idea would be to call this (if you can) in a loop with > 10 images forced to 300x400. Crashing then would be more evidence of a leak. Meantime, if its just memory pressure due to the one image (not a leak) lmk if you need help doing a smarter computation on size – danh Sep 17 '19 at 13:20
  • I tried looping and scaling down same image and storing them in an array. It is working fine without crash. I am sure the crash is due to a single image with high dimension. Can you please suggest a way? – George Sep 17 '19 at 13:47
  • See edit for advice on scaling. It may be that the app is already under memory pressure at the point where the big image is processed. In the vc that's active, it may be worthwhile to implement didReceiveMemoryWarning. For starters, just log there to see if it fires. – danh Sep 17 '19 at 14:03
  • yes the didReceiveMemoryWarning is fired. How to check the reason for this memory pressure? – George Sep 17 '19 at 14:16
  • That's a big topic. Maybe start here https://developer.apple.com/library/archive/documentation/Performance/Conceptual/ManagingMemory/Articles/FindingPatterns.html. It might also be worthwhile to get fixed in your mind the difference between a leak and memory pressure. The former is inadvertent retention, most often cause by a retain cycle, leaving allocations around that the app can't reach. The latter is an app working "correctly" but making demands on memory that the system can't handle. – danh Sep 17 '19 at 14:27