CGImageRef Memory leak after release and autoreleasepool - objective-c

Im trying to implement show image from local content but for some reason memory Would not get freed.
#autoreleasepool {
ALAssetRepresentation *rep = [myasset defaultRepresentation];
CGImageRef iref = [rep fullResolutionImage];
UIImage *largeimage = [UIImage imageWithCGImage:iref scale:[UIScreen mainScreen].scale orientation:(UIImageOrientation)rep.orientation];
CFRelease(iref);
self.imageView.image = largeimage;
largeimage = nil;
}
As suggested ,i am used
CGImageRelease(imageRef);
but still i am got an memory leak. After that i am wrap code with an
#autoreleasepool {}
block but that also not solve my problem.
What chould I do ?

I think the issue is when you assign the image to your image view. Can you try resizing the image before assigning it to image view?
Use this method
- (UIImage *)resizeImage:(UIImage *)sourceImage toSize:(CGSize)newSize
{
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[sourceImage drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}

Related

Objective-C: Memory leak with CGDataProviderCopyData

With the help of the instrument, it is shown that CGDataProviderCopyData is using too much memory. How to fix the issue?
-(UIImage*)imageNamed:(NSString*)name {
UIImage *uiimage = [UIImage imageNamed:name];
CGImageRef originalImage = [uiimage CGImage];
CFDataRef imageData = CGDataProviderCopyData(
CGImageGetDataProvider(originalImage));
CGDataProviderRef imageDataProvider = CGDataProviderCreateWithCFData(imageData);
CFRelease(imageData);
CGImageRef image = CGImageCreate(
CGImageGetWidth(originalImage),
CGImageGetHeight(originalImage),
CGImageGetBitsPerComponent(originalImage),
CGImageGetBitsPerPixel(originalImage),
CGImageGetBytesPerRow(originalImage),
CGImageGetColorSpace(originalImage),
CGImageGetBitmapInfo(originalImage),
imageDataProvider,
CGImageGetDecode(originalImage),
CGImageGetShouldInterpolate(originalImage),
CGImageGetRenderingIntent(originalImage));
CGDataProviderRelease(imageDataProvider);
return [UIImage imageWithCGImage:image];
}
Finally, I was able to solve this. Actually there were few extra unnecessary steps which were causing memory leaks. Here is the updated function:
-(UIImage*)imageNamed:(NSString*)name {
UIImage *uiimage = [UIImage imageNamed:name];
CGImageRef originalImage = [uiimage CGImage];
CGImageRef image = CGImageCreate(
CGImageGetWidth(originalImage),
CGImageGetHeight(originalImage),
CGImageGetBitsPerComponent(originalImage),
CGImageGetBitsPerPixel(originalImage),
CGImageGetBytesPerRow(originalImage),
CGImageGetColorSpace(originalImage),
CGImageGetBitmapInfo(originalImage),
CGImageGetDataProvider(originalImage),
CGImageGetDecode(originalImage),
CGImageGetShouldInterpolate(originalImage),
CGImageGetRenderingIntent(originalImage));
return [UIImage imageWithCGImage:image];
}
CGImageGetDataProvider(originalImage) returns CGDataProviderRef and that is required as 8th parameter in CGImageCreate function. Above steps to copy image data and then creating CGDataProviderRef were unnecessary.
The problem is that you never release image.
Update your code as follows:
-(UIImage*)imageNamed:(NSString*)name {
UIImage *uiimage = [UIImage imageNamed:name];
CGImageRef originalImage = [uiimage CGImage];
CFDataRef imageData = CGDataProviderCopyData(
CGImageGetDataProvider(originalImage));
CGDataProviderRef imageDataProvider = CGDataProviderCreateWithCFData(imageData);
CFRelease(imageData);
CGImageRef image = CGImageCreate(
CGImageGetWidth(originalImage),
CGImageGetHeight(originalImage),
CGImageGetBitsPerComponent(originalImage),
CGImageGetBitsPerPixel(originalImage),
CGImageGetBytesPerRow(originalImage),
CGImageGetColorSpace(originalImage),
CGImageGetBitmapInfo(originalImage),
imageDataProvider,
CGImageGetDecode(originalImage),
CGImageGetShouldInterpolate(originalImage),
CGImageGetRenderingIntent(originalImage));
CGDataProviderRelease(imageDataProvider);
UIImage *result = [UIImage imageWithCGImage:image];
CGImageRelease(image);
return result;
}

drawAtPoint is eating memory

I am working on an iOS app. I need crop an image which is generate from PDF.
Sometimes, the image resolution can be very big.
I use follow code to generate the cropped image. My problem is the memory is increasing all the time. Never released.
- (UIImage *)croppedImageWithFrame:(CGRect)frame angle:(NSInteger)angle{
UIImage *croppedImage = nil;
CGPoint drawPoint = CGPointZero;
UIGraphicsBeginImageContextWithOptions(frame.size, YES, self.scale);
{
CGContextRef context = UIGraphicsGetCurrentContext();
//To conserve memory in not needing to completely re-render the image re-rotated,
//map the image to a view and then use Core Animation to manipulate its rotation
if (angle != 0) {
UIImageView *imageView = [[UIImageView alloc] initWithImage:self];
imageView.layer.minificationFilter = #"nearest";
imageView.layer.magnificationFilter = #"neareset";
imageView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, angle * (M_PI/180.0f));
CGRect rotatedRect = CGRectApplyAffineTransform(imageView.bounds, imageView.transform);
UIView *containerView = [[UIView alloc] initWithFrame:(CGRect){CGPointZero, rotatedRect.size}];
[containerView addSubview:imageView];
imageView.center = containerView.center;
CGContextTranslateCTM(context, -frame.origin.x, -frame.origin.y);
[containerView.layer renderInContext:context];
}
else {
CGContextTranslateCTM(context, -frame.origin.x, -frame.origin.y);
[self drawAtPoint:drawPoint];
}
croppedImage = UIGraphicsGetImageFromCurrentImageContext();
}
UIGraphicsEndImageContext();
return croppedImage;
}
When I debug,
It burns 100MB in line
UIGraphicsBeginImageContextWithOptions(frame.size, YES, self.scale);
then when run into follow line, it burn another 150MB
[self drawAtPoint:drawPoint];
When run to follow line, it release 100MB
UIGraphicsEndImageContext();
after it's done, the 150MB never released
I thought UIGraphicsEndImageContext() should release all the 250MB. Why it's not?

UIImage from CIImage - Data length is zero?

I'm using an AVCaptureVideoDataOutput along with its delegate method to manipulate video frames. In the delegate method, I am using the sampleBuffer to create a CIImage, and from here I crop the CIImage, convert it to a UIImage and display it. Unfortunately, I need to determine the file-size of this new UIImage, but it's returning 0. The code works, the image is cropped beautifully, everything. I just don't see why it has no data!
Why might this be? Relevant code follows:
//In delegate method, given sampleBuffer...
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CFDictionaryRef attachments = CMCopyDictionaryOfAttachments(kCFAllocatorDefault,
sampleBuffer, kCMAttachmentMode_ShouldPropagate);
CIImage *ciImage = [[CIImage alloc] initWithCVPixelBuffer:pixelBuffer
options:(NSDictionary *)attachments];
...
dispatch_async(dispatch_get_main_queue(), ^(void) {
CGRect rect = [self drawFaceBoxesForFeatures:features forVideoBox:clap
orientation:curDeviceOrientation];
CIImage *cropped = [ciImage imageByCroppingToRect:rect];
UIImage *image = [[UIImage alloc] initWithCIImage:cropped];
NSData *data = UIImageJPEGRepresentation(image, 1);
NSLog(#"Image size is %d", data.length); //returns 0???
[imageView setImage:image];
[image release];
});
I had the same Problem, but with simple filtered images.
I stumbled upon this and it solved the issue. After this, I was able to save my image.
CGSize size = self.originalImage.size;
CGRect rect;
rect.origin = CGPointZero;
rect.size = size;
UIGraphicsBeginImageContext(size);
[[UIImage imageWithCIImage:self.filteredImage] drawInRect:rect];
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData * jpegData = UIImageJPEGRepresentation(image, 1.0);
But I only needed this two lines in the "ImageContext"

draw one image above another code not working

I have 2 images of same size and I need to make 3d picture in which 2nd is above 1st
I have UIImage class extension with following method
+(UIImage*)imageFrom2Images:(UIImage *)img1 with:(UIImage *)img2 {
UIGraphicsBeginImageContext(img1.size);
CGContextRef context = UIGraphicsGetCurrentContext();
UIGraphicsPushContext(context);
[img1 drawAtPoint:CGPointMake(0, 0)];
[img2 drawAtPoint:CGPointMake(0, 0)];
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsPopContext();
UIGraphicsEndImageContext();
return resultingImage;
}
I've tried to save resulting image in Photo Album but it calls error and not working..It seems that this method is wrong.
UIImage*t1=[self.mw.imageFIFO lastObject];
UIImage* test=[UIImage imageFrom2Images:self.imageView.image with:t1];
UIImageWriteToSavedPhotosAlbum(test, self,
#selector(image:didFinishSavingWithError:contextInfo:), nil);
The error message I am getting is:
Error Domain=ALAssetsLibraryErrorDomain Code=-3304 "Failed to encode image for saved photos."
UserInfo=0x9398960 {NSUnderlyingError=0x935e760 "Failed to encode image for saved photos.",
NSLocalizedDescription=Failed to encode image for saved photos.}
You should never need to get the current graphics context and then push it - you're just duplicating the top of the context stack. It would be nice if it still worked, but it doesn't. Remove the calls to UIGraphicsPushContext and UIGraphicsPopContext, and it works as intended.
+(UIImage*)imageFrom2Images:(UIImage *)img1 with:(UIImage *)img2 {
UIGraphicsBeginImageContext(img1.size);
[img1 drawAtPoint:CGPointMake(0, 0)];
[img2 drawAtPoint:CGPointMake(0, 0)];
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultingImage;
}
To detect these problems in future, check the output from imageFrom2Images. As originally implemented, it returned nil, so it's not surprising that the following calls didn't know what to do. (If it had returned an image object, the next step would be to display it inside a UIImageView, to make sure it's the correct image.)
This method should do it:
-(UIImage *)drawFirstImage:(UIImage*)firstImage afterSecondImage:(UIImage *)secondImage
{
float finalWidth=MAX(firstImage.size.width,secondImage.size.width);
float finalHeight=firstImage.size.height + secondImage.size.height;
CGSize finalSize=CGSizeMake(finalWidth, finalHeight);
UIGraphicsBeginImageContext(finalSize);
[firstImage drawInRect:CGRectMake(0, 0, firstImage.size.width, firstImage.size.height)];
[secondImage drawInRect:CGRectMake(0, firstImage.size.height, secondImage.size.width, secondImage.size.height)];
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultImage;
}
You can use it as:
UIImage *resultImage= [self drawFirstImage:image1 afterSecondImage:image2];
Here it is a category implementation:
UIImage+MyExtensions.h
#import <UIKit/UIKit.h>
#interface UIImage (MyExtensions)
-(UIImage *)attachImageBelow:(UIImage *)secondImage;
#end
UIImage+MyExtensions.m
#import "UIImage+MyExtensions.h"
#implementation UIImage (MyExtensions)
-(UIImage *)attachImageBelow:(UIImage *)secondImage{
float finalWidth=MAX(self.size.width,secondImage.size.width);
float finalHeight=self.size.height + secondImage.size.height;
CGSize finalSize=CGSizeMake(finalWidth, finalHeight);
UIGraphicsBeginImageContext(finalSize);
[self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
[secondImage drawInRect:CGRectMake(0, self.size.height, secondImage.size.width, secondImage.size.height)];
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultImage;
}
You can use it like this:
#import "UIImage+MyExtensions.h"
UIImage *resultImage= [image1 attachImageBelow:image2];

Resize and Save NSImage?

I have an NSImageView which I get an image for from an NSOpenPanel. That works great.
Now, how can I take that NSImage, half its size and save it as the same format in the same directory as the original as well?
If you can help at all with anything I'd appreciate it, thanks.
Check the ImageCrop sample project from Matt Gemmell:
http://mattgemmell.com/source/
Nice example how to resize / crop images.
Finally you can use something like this to save the result (dirty sample):
// Write to TIF
[[resultImg TIFFRepresentation] writeToFile:#"/Users/Anne/Desktop/Result.tif" atomically:YES];
// Write to JPG
NSData *imageData = [resultImg TIFFRepresentation];
NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:imageData];
NSDictionary *imageProps = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:0.9] forKey:NSImageCompressionFactor];
imageData = [imageRep representationUsingType:NSJPEGFileType properties:imageProps];
[imageData writeToFile:#"/Users/Anne/Desktop/Result.jpg" atomically:NO];
Since NSImage objects are immutable you will have to:
Create a Core Graphics context the size of the new image.
Draw the NSImage into the CGContext. It should automatically scale it for you.
Create an NSImage from that context
Write out the new NSImage
Don't forget to release any temporary objects you allocated.
There are definitely other options, but this is the first one that came to mind.
+(NSImage*) resize:(NSImage*)aImage scale:(CGFloat)aScale
{
NSImageView* kView = [[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, aImage.size.width * aScale, aImage.size.height* aScale)];
[kView setImageScaling:NSImageScaleProportionallyUpOrDown];
[kView setImage:aImage];
NSRect kRect = kView.frame;
NSBitmapImageRep* kRep = [kView bitmapImageRepForCachingDisplayInRect:kRect];
[kView cacheDisplayInRect:kRect toBitmapImageRep:kRep];
NSData* kData = [kRep representationUsingType:NSJPEGFileType properties:nil];
return [[NSImage alloc] initWithData:kData];
}
Here is a specific implementation
-(NSImage*)resizeImage:(NSImage*)input by:(CGFloat)factor
{
NSSize size = NSZeroSize;
size.width = input.size.width*factor;
size.height = input.size.height*factor;
NSImage *ret = [[NSImage alloc] initWithSize:size];
[ret lockFocus];
NSAffineTransform *transform = [NSAffineTransform transform];
[transform scaleBy:factor];
[transform concat];
[input drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];
[ret unlockFocus];
return [ret autorelease];
}
Keep in mind that this is pixel based, with HiDPI the scaling must be taken into account, it is simple to obtain :
-(CGFloat)pixelScaling
{
NSRect pixelBounds = [self convertRectToBacking:self.bounds];
return pixelBounds.size.width/self.bounds.size.width;
}
Apple has source code for downscaling and saving images found here
http://developer.apple.com/library/mac/#samplecode/Reducer/Introduction/Intro.html
Here is some code that makes a more extensive use of Core Graphics than other answers. It's made according to hints in Mark Thalman's answer to this question.
This code downscales an NSImage based on a target image width. It's somewhat nasty, but still useful as an extra sample for documenting how to draw an NSImage in a CGContext, and how to write contents of CGBitmapContext and CGImage into a file.
You may want to add extra error checking. I didn't need it for my use case.
- (void)generateThumbnailForImage:(NSImage*)image atPath:(NSString*)newFilePath forWidth:(int)width
{
CGSize size = CGSizeMake(width, image.size.height * (float)width / (float)image.size.width);
CGColorSpaceRef rgbColorspace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast;
CGContextRef context = CGBitmapContextCreate(NULL, size.width, size.height, 8, size.width * 4, rgbColorspace, bitmapInfo);
NSGraphicsContext * graphicsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO];
[NSGraphicsContext setCurrentContext:graphicsContext];
[image drawInRect:NSMakeRect(0, 0, size.width, size.height) fromRect:NSMakeRect(0, 0, image.size.width, image.size.height) operation:NSCompositeCopy fraction:1.0];
CGImageRef outImage = CGBitmapContextCreateImage(context);
CFURLRef outURL = (CFURLRef)[NSURL fileURLWithPath:newFilePath];
CGImageDestinationRef outDestination = CGImageDestinationCreateWithURL(outURL, kUTTypeJPEG, 1, NULL);
CGImageDestinationAddImage(outDestination, outImage, NULL);
if(!CGImageDestinationFinalize(outDestination))
{
NSLog(#"Failed to write image to %#", newFilePath);
}
CFRelease(outDestination);
CGImageRelease(outImage);
CGContextRelease(context);
CGColorSpaceRelease(rgbColorspace);
}
To resize image
- (NSImage *)scaleImage:(NSImage *)anImage newSize:(NSSize)newSize
{
NSImage *sourceImage = anImage;
if ([sourceImage isValid])
{
if (anImage.size.width == newSize.width && anImage.size.height == newSize.height && newSize.width <= 0 && newSize.height <= 0) {
return anImage;
}
NSRect oldRect = NSMakeRect(0.0, 0.0, anImage.size.width, anImage.size.height);
NSRect newRect = NSMakeRect(0,0,newSize.width,newSize.height);
NSImage *newImage = [[NSImage alloc] initWithSize:newSize];
[newImage lockFocus];
[sourceImage drawInRect:newRect fromRect:oldRect operation:NSCompositeCopy fraction:1.0];
[newImage unlockFocus];
return newImage;
}
}