Melvin
Melvin

Reputation: 3431

Huge memory peak - CGContextDrawImage

I use this piece of code to scale and rotate images taken with the photo camera. When I use this I can see a huge memory peak. Something like 20 MB. When I use the instruments I can see that this line:

CGContextDrawImage(ctxt, orig, self.CGImage);

holds 20 MB. Is this normal for full resolution photos? The iPhone 4S can handle it. but older devices crashes because of this code.

After I rescale the image I need it in NSData, so I use the UIImageJPEGRepresentation() method. This together makes the memory peak even higher. It goes to 70 MB in memory usage for several seconds.

And yes, I did read almost all the iOS camera related question about memory usage. But no answer out there.

// WBImage.mm -- extra UIImage methods
// by allen brunson  march 29 2009

#include "WBImage.h"

static inline CGFloat degreesToRadians(CGFloat degrees)
{
    return M_PI * (degrees / 180.0);
}

static inline CGSize swapWidthAndHeight(CGSize size)
{
CGFloat  swap = size.width;

size.width  = size.height;
size.height = swap;

return size;
}

@implementation UIImage (WBImage)

// rotate an image to any 90-degree orientation, with or without mirroring.
// original code by kevin lohman, heavily modified by yours truly.
// http://blog.logichigh.com/2008/06/05/uiimage-fix/

-(UIImage*)rotate:(UIImageOrientation)orient
{
CGRect             bnds = CGRectZero;
UIImage*           copy = nil;
CGContextRef       ctxt = nil;
CGRect             rect = CGRectZero;
CGAffineTransform  tran = CGAffineTransformIdentity;

bnds.size = self.size;
rect.size = self.size;

switch (orient)
{
    case UIImageOrientationUp:
        return self;

    case UIImageOrientationUpMirrored:
        tran = CGAffineTransformMakeTranslation(rect.size.width, 0.0);
        tran = CGAffineTransformScale(tran, -1.0, 1.0);
        break;

    case UIImageOrientationDown:
        tran = CGAffineTransformMakeTranslation(rect.size.width,
                                                rect.size.height);
        tran = CGAffineTransformRotate(tran, degreesToRadians(180.0));
        break;

    case UIImageOrientationDownMirrored:
        tran = CGAffineTransformMakeTranslation(0.0, rect.size.height);
        tran = CGAffineTransformScale(tran, 1.0, -1.0);
        break;

    case UIImageOrientationLeft:
        bnds.size = swapWidthAndHeight(bnds.size);
        tran = CGAffineTransformMakeTranslation(0.0, rect.size.width);
        tran = CGAffineTransformRotate(tran, degreesToRadians(-90.0));
        break;

    case UIImageOrientationLeftMirrored:
        bnds.size = swapWidthAndHeight(bnds.size);
        tran = CGAffineTransformMakeTranslation(rect.size.height,
                                                rect.size.width);
        tran = CGAffineTransformScale(tran, -1.0, 1.0);
        tran = CGAffineTransformRotate(tran, degreesToRadians(-90.0));
        break;

    case UIImageOrientationRight:
        bnds.size = swapWidthAndHeight(bnds.size);
        tran = CGAffineTransformMakeTranslation(rect.size.height, 0.0);
        tran = CGAffineTransformRotate(tran, degreesToRadians(90.0));
        break;

    case UIImageOrientationRightMirrored:
        bnds.size = swapWidthAndHeight(bnds.size);
        tran = CGAffineTransformMakeScale(-1.0, 1.0);
        tran = CGAffineTransformRotate(tran, degreesToRadians(90.0));
        break;

    default:
        // orientation value supplied is invalid
        assert(false);
        return nil;
}

UIGraphicsBeginImageContext(rect.size);
ctxt = UIGraphicsGetCurrentContext();

switch (orient)
{
    case UIImageOrientationLeft:
    case UIImageOrientationLeftMirrored:
    case UIImageOrientationRight:
    case UIImageOrientationRightMirrored:
        CGContextScaleCTM(ctxt, -1.0, 1.0);
        CGContextTranslateCTM(ctxt, -rect.size.height, 0.0);
        break;

    default:
        CGContextScaleCTM(ctxt, 1.0, -1.0);
        CGContextTranslateCTM(ctxt, 0.0, -rect.size.height);
        break;
}

CGContextConcatCTM(ctxt, tran);
CGContextDrawImage(ctxt, bnds, self.CGImage);

copy = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

return copy;
}

-(UIImage*)rotateAndScaleFromCameraWithMaxSize:(CGFloat)maxSize
{
UIImage*  imag = self;

imag = [imag rotate:imag.imageOrientation];
imag = [imag scaleWithMaxSize:maxSize];

return imag;
}

-(UIImage*)scaleWithMaxSize:(CGFloat)maxSize
{
return [self scaleWithMaxSize:maxSize quality:kCGInterpolationHigh];
}

-(UIImage*)scaleWithMaxSize:(CGFloat)maxSize
                quality:(CGInterpolationQuality)quality
{
CGRect        bnds = CGRectZero;
UIImage*      copy = nil;
CGContextRef  ctxt = nil;
CGRect        orig = CGRectZero;
CGFloat       rtio = 0.0;
CGFloat       scal = 1.0;

bnds.size = self.size;
orig.size = self.size;
rtio = orig.size.width / orig.size.height;

if ((orig.size.width <= maxSize) && (orig.size.height <= maxSize))
{
    return self;
}

if (rtio > 1.0)
{
    bnds.size.width  = maxSize;
    bnds.size.height = maxSize / rtio;
}
else
{
    bnds.size.width  = maxSize * rtio;
    bnds.size.height = maxSize;
}

UIGraphicsBeginImageContext(bnds.size);
ctxt = UIGraphicsGetCurrentContext();

scal = bnds.size.width / orig.size.width;  
CGContextSetInterpolationQuality(ctxt, quality);

CGContextScaleCTM(ctxt, scal, -scal);
CGContextTranslateCTM(ctxt, 0.0, -orig.size.height);

CGContextDrawImage(ctxt, orig, self.CGImage);

copy = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

return copy;
}

@end

Upvotes: 4

Views: 3284

Answers (3)

Melvin
Melvin

Reputation: 3431

I ended up using imageIO, less memory!

-(UIImage *)resizeImageToMaxDimension: (float) dimension withPaht: (NSString *)path
{

NSURL *imageUrl = [NSURL fileURLWithPath:path];
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((__bridge CFURLRef)imageUrl, NULL);

NSDictionary *thumbnailOptions = [NSDictionary dictionaryWithObjectsAndKeys:
                                  (id)kCFBooleanTrue, kCGImageSourceCreateThumbnailWithTransform,
                                  kCFBooleanTrue, kCGImageSourceCreateThumbnailFromImageAlways,
                                  [NSNumber numberWithFloat:dimension], kCGImageSourceThumbnailMaxPixelSize,
                                  nil];
CGImageRef thumbnail = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, (__bridge CFDictionaryRef)thumbnailOptions);

UIImage *resizedImage = [UIImage imageWithCGImage:thumbnail];

CFRelease(thumbnail);
CFRelease(imageSource);

return resizedImage;

}

Upvotes: 3

user523234
user523234

Reputation: 14834

Insert this at the end of your methods and before the return copy;:

CGContextRelease(ctxt);

Upvotes: 0

Andrea
Andrea

Reputation: 26383


that's correct it comes from the picture that you shot with your camera, older devices use cameras with less resolution, that means that images taken with an iPhone 3g are smaller in resolution (thus size) respect the one that you have on your iPhone4s. Images are usually compressed but when they open up in memory for some sort of operation they must be decompresses, the size that they need is really bigger that the one in a file, because is the number_of_pixel_in_row*number_of_pixel_in_height*byte_for_pixel if I remember well.
Bye, Andrea

Upvotes: 1

Related Questions