VM: CG raster Data memory keep growing - ios

so i am trying to make an app that will let the user change the color of the UIImage, for that i am using this function i found
- (UIImage *)imageWithTintColor:(UIColor *)color fraction:(CGFloat)fraction
{
if (color)
{
UIImage *image;
if ([UIScreen instancesRespondToSelector:#selector(scale)])
{
UIGraphicsBeginImageContextWithOptions([self size], NO, 0.f);
}
else
{
UIGraphicsBeginImageContext([self size]);
}
CGRect rect = CGRectZero;
rect.size = [self size];
[color set];
UIRectFill(rect);
[self drawInRect:rect blendMode:kCGBlendModeDestinationIn alpha:1.0];
if (fraction > 0.0)
{
[self drawInRect:rect blendMode:kCGBlendModeSourceAtop alpha:fraction];
}
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
return self;
}
everything works but the CG raster Data is growing in memory

I found the problem, and it was my bad logic, i am using 2 views one to show and one to work with ex:resize, move, rotate. And each time i was addingSubview to both where one of them need to hold just 1 at a time, a simple:
for (UIView *view in 2cndView.subviews)
{
[view removeFromSuperview];
}
did the trick for me

I have been fighting with my app, that suddenly would not launch properly, for some time now. It turned out that when I had switched a number of images' Render as to Template in the Image asset file, it caused the app to totally bomb out. CG Raster Data was growing exponentially and finally caused the app to stop and Xcode just said
Lost connection with iPhone.. check connections etc
It would appear that during every launch the images get reprocessed for this 'Template' setting, which consumed a disgusting amount of RAM and actually left it unable to boot. To solve this, I lowered the resolution of the images - as simple as that.

Related

Receive Memory warning in combining two UIImage

I try to combine two UIImage with the following code:
- (void)combineImage:(UIImage *)image WithFrame:(CGRect)frame Completion:(ImageProcessorCompletionBlock)block {
__weak typeof(self) wSelf = self;
dispatch_async(_queue, ^{
if (wSelf) {
typeof(wSelf) sSelf = wSelf;
UIGraphicsBeginImageContextWithOptions(sSelf.originalImage.size, NO, 0.0);
[sSelf.originalImage drawInRect:CGRectMake(0, 0, sSelf.originalImage.size.width, sSelf.originalImage.size.height)];
[image drawInRect:frame];
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
dispatch_async(dispatch_get_main_queue(), ^{
if (block) {
block(result);
}
});
}
});
}
That works but when I check out the usage of memory, it scared me. Every time I run the method the memory rise up and never release. Sometimes I receive the memory warning. Can anyone tell me why and give me a solution to solve the problem? Thanks a lot!
Finally I figure out the problem.
UIGraphicsBeginImageContextWithOptions(sSelf.originalImage.size, NO, 0.0);
The first parameter is the size of the image and the last one is the scale factor. At the beginning I have already set the image size same as the original one. But I also set the scale as 0.0, which means it is set to the scale factor of the device’s main screen. So the result image is enlarged.
If I run the code several times, the result's size gets bigger and bigger, finally it use up the memory and I receive the warning.

iOS8 scale glitch when calling drawViewHierarchyInRect afterScreenUpdates:YES

I was converting a project from iOS7 to iOS8 which uses custom transitions and needs to capture the modal after it finishes loading afterScreenUpdates:YES and was seeing that the entire screen scale up for a second and scale back down. I also see this happening in the Flickr app for iOS between sections and on Yelp app when transitioning to a photo on iOS8.
UIGraphicsBeginImageContextWithOptions(self.view.frame.size, YES, 22.0);
[self.view drawViewHierarchyInRect:self.view.frame afterScreenUpdates:YES];
UIGraphicsEndImageContext();
Adding a larger scale factor helps emphasize the glitch more... but i'm just calling this on a button press in the example.
EDIT This appears to happen on iPhone 6 and 6 plus not on the 5.
Sample project github
Do you NEED it to draw after the screen updates? because I'm using:
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:NO];
and it seems to work fine on iOS7 and iOS8. I imagine this isn't a great solution for capturing images regularly (like multiple times a second) but it seems to work for a once off blur.
You have to provide #3x launch images for the 6 and 6 plus. The 6 being scaled at 750x1334, and the 6 plus image being scaled at 1242x2208.
Even it looks like bug in API, you can call drawViewHierarchyInRect with afterScreenUpdates set to NO to build snapshot AFTER screen updates if you use cunstruction like this:
typedef void (^CompletionHandlerWithId)(id result);
-(void)imageContaining:(CGRect)rect afterScreenUpdates:(bool)afterScreenUpdates opaque:(BOOL)opaque completion:(CompletionHandlerWithId)completion
{
bool success __block;
UIImage *snapshotImage __block = nil;
CompletionHandler block = ^{
// Create the image
UIGraphicsBeginImageContextWithOptions(self.bounds.size, opaque, [[UIScreen mainScreen] scale]);
success = [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:NO];
if (success)
{
snapshotImage = UIGraphicsGetImageFromCurrentImageContext();
CGImageRef imageRef = CGImageCreateWithImageInRect(
[snapshotImage CGImage],
CGRectMake(
snapshotImage.scale*rect.origin.x,
snapshotImage.scale*rect.origin.y,
snapshotImage.scale*rect.size.width,
snapshotImage.scale*rect.size.height));
// or use the UIImage wherever you like
snapshotImage = [UIImage imageWithCGImage:imageRef scale:snapshotImage.scale orientation:UIImageOrientationUp];
CGImageRelease(imageRef);
}
UIGraphicsEndImageContext();
if (completion)
{
if (! success)
{
NSLog(#"Error: [UIView drawViewHierarchyInRect] failed on %#", self);
(completion)(nil);
}
else
{
NSLog(#"Success: [UIView drawViewHierarchyInRect] on %#", self);
(completion)(snapshotImage);
}
}
};
if (afterScreenUpdates)
[CATransaction setCompletionBlock:^{
(block)();
}];
else
(block)();
}
This bug also exists when you run on an iPad2 running iOS7.
Fix: set afterScreenUpdates: to NO
My app has some moving UIButtons, so I don't allow the blur transition until after the movement has stopped. As far as I have found so far, there is no difference in YES or NO.
Appears to be fixed in iOS9 / XCODE 7 builds
I found a solution for me to solve this problem.
I add #3x launch images to my project. And choose launch Screen file to "Main".
This will make app run at original resolution, smaller bounds when run at iphone6, but not glitch when calling drawViewHierarchyInRect. Like this.
Then, scale my view to fullscreen when viewDidLoad.
- (void)viewDidLoad{
[super viewDidLoad];
UIScreen *mainScreen = [UIScreen mainScreen];
CGRect tempFrame=mainScreen.bounds;
double aspect=tempFrame.size.width/320;
self.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, aspect, aspect);
}
Hope helpful :)

UIGraphicsBeginImageContextWithOptions very masive memory disaster

I have this line of code to take screenshot of uiview:
- (UIImage *)imageWithView:(UIView *)view {
UIImage *viewImage = nil;
float height;
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if(orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight){
height = view.frame.size.height;
}else{
height = 730;
}
UIGraphicsBeginImageContextWithOptions(CGSizeMake(view.frame.size.width, height), YES, 0.0);
[view drawViewHierarchyInRect:CGRectMake(0, -60, view.bounds.size.width, height) afterScreenUpdates:YES];
viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return viewImage;
}
... in this uiview is UIWebView.
This - (UIImage *)imageWithView:(UIView *)view method I call in everytime uiwebview finish load. And there's is my problem: When I open some small webpages like google it method takes some kb of memory, but when I open something biger like bbc, cnn, yahoo or stackoverflow, it can take up to 80mb memory usage.
There is snapshoot of instrument when cnn.com was opened.
After few seconds it realeses, but I don't want that big memory usage, because you might imagine, how useless becomes uiwebview in these seconds.
So, what is your suggestions, how to take screenshoot of uiwebview, without so big memory usage, I don't even need this uiimage in good quality, because I put it in 120*80 uiimageview in one of the screen corners.
The last parameter in UIGraphicsBeginImageContextWithOptions is scale. If you know you only need a low resolution rendering of the web view, try setting a value for scale less than 1.0.
UIGraphicsBeginImageContextWithOptions(CGSizeMake(view.frame.size.width, height), YES, 0.2);

Blur screen with iOS 7's snapshot API

I believe the NDA is down, so I can ask this question. I have a UIView subclass:
BlurView *blurredView = ((BlurView *)[self.view snapshotViewAfterScreenUpdates:NO]);
blurredView.frame = self.view.frame;
[self.view addSubview:blurredView];
It does its job so far in capturing the screen, but now I want to blur that view. How exactly do I go about this? From what I've read I need to capture the current contents of the view (context?!) and convert it to CIImage (no?) and then apply a CIGaussianBlur to it and draw it back on the view.
How exactly do I do that?
P.S. The view is not animated, so it should be OK performance wise.
EDIT: Here is what I have so far. The problem is that I can't capture the snapshot to a UIImage, I get a black screen. But if I add the view as a subview directly, I can see the snapshot is there.
// Snapshot
UIView *view = [self.view snapshotViewAfterScreenUpdates:NO];
// Convert to UIImage
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Apply the UIImage to a UIImageView
BlurView *blurredView = [[BlurView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
[self.view addSubview:blurredView];
blurredView.imageView.image = img;
// Black screen -.-
BlurView.m:
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.imageView = [[UIImageView alloc] init];
self.imageView.frame = CGRectMake(20, 20, 200, 200);
[self addSubview:self.imageView];
}
return self;
}
Half of this question didn't get answered, so I thought it worth adding.
The problem with UIScreen's
- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates
and UIView's
- (UIView *)resizableSnapshotViewFromRect:(CGRect)rect
afterScreenUpdates:(BOOL)afterUpdates
withCapInsets:(UIEdgeInsets)capInsets
Is that you can't derive a UIImage from them - the 'black screen' problem.
In iOS7 Apple provides a third piece of API for extracting UIImages, a method on UIView
- (BOOL)drawViewHierarchyInRect:(CGRect)rect
afterScreenUpdates:(BOOL)afterUpdates
It is not as fast as snapshotView, but not bad compared to renderInContext (in the example provided by Apple it is five times faster than renderInContext and three times slower than snapshotView)
Example use:
UIGraphicsBeginImageContextWithOptions(image.size, NULL, 0);
[view drawViewHierarchyInRect:rect];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Then to get a blurred version
UIImage* lightImage = [newImage applyLightEffect];
where applyLightEffect is one of those Blur category methods on Apple's UIImage+ImageEffects category mentioned in the accepted answer (the enticing link to this code sample in the accepted answer doesn't work, but this one will get you to the right page: the file you want is iOS_UIImageEffects).
The main reference is to WWDC2013 session 226, Implementing Engaging UI on iOS
By the way, there is an intriguing note in Apple's reference docs for renderInContext that hints at the black screen problem..
Important: The OS X v10.5 implementation of this method does not support the entire Core Animation composition model. QCCompositionLayer, CAOpenGLLayer, and QTMovieLayer layers are not rendered. Additionally, layers that use 3D transforms are not rendered, nor are layers that specify backgroundFilters, filters, compositingFilter, or a mask values. Future versions of OS X may add support for rendering these layers and properties.
The note hasn't been updated since 10.5, so I guess 'future versions' may still be a while off, and we can add our new CASnapshotLayer (or whatever) to the list.
Sample Code from WWDC ios_uiimageeffects
There is a UIImage category named UIImage+ImageEffects
Here is its API:
- (UIImage *)applyLightEffect;
- (UIImage *)applyExtraLightEffect;
- (UIImage *)applyDarkEffect;
- (UIImage *)applyTintEffectWithColor:(UIColor *)tintColor;
- (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius
tintColor:(UIColor *)tintColor
saturationDeltaFactor:(CGFloat)saturationDeltaFactor
maskImage:(UIImage *)maskImage;
For legal reason I can't show the implementation here, there is a demo project in it. should be pretty easy to get start with.
To summarize how to do this with foundry's sample code, use the following:
I wanted to blur the entire screen just slightly so for my purposes so I'll use the main screen bounds.
CGRect screenCaptureRect = [UIScreen mainScreen].bounds;
UIView *viewWhereYouWantToScreenCapture = [[UIApplication sharedApplication] keyWindow];
//screen capture code
UIGraphicsBeginImageContextWithOptions(screenCaptureRect.size, NO, [UIScreen mainScreen].scale);
[viewWhereYouWantToScreenCapture drawViewHierarchyInRect:screenCaptureRect afterScreenUpdates:NO];
UIImage *capturedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//blur code
UIColor *tintColor = [UIColor colorWithWhite:1.0 alpha:0];
UIImage *blurredImage = [capturedImage applyBlurWithRadius:1.5 tintColor:tintColor saturationDeltaFactor:1.2 maskImage:nil];
//or use [capturedImage applyLightAffect] but I thought that was too much for me
//use blurredImage in whatever way you so desire!
Notes on the screen capture part
UIGraphicsBeginImageContextWithOptions() 2nd argument is opacity. It should be NO unless you have nothing with any alpha other than 1. If you return yes the screen capture will not look at transparency values so it will go faster but will probably be wrong.
UIGraphicsBeginImageContextWithOptions() 3rd argument is the scale. Probably want to put in the scale of the device like I did to make sure and differentiate between retina and non-retina. But I haven't really tested this and I think 0.0f also works.
drawViewHierarchyInRect:afterScreenUpdates: watch out what you return for the screen updates BOOL. I tried to do this right before backgrounding and if I didn't put NO the app would go crazy with glitches when I returned to the foreground. You might be able to get away with YES though if you're not leaving the app.
Notes on blurring
I have a very light blur here. Changing the blurRadius will make it blurrier, and you can change the tint color and alpha to make all sorts of other effects.
Also you need to add a category for the blur methods to work...
How to add the UIImage+ImageEffects category
You need to download the category UIImage+ImageEffects for the blur to work. Download it here after logging in: https://developer.apple.com/downloads/index.action?name=WWDC%202013
Search for "UIImageEffects" and you'll find it. Just pull out the 2 necessary files and add them to your project. UIImage+ImageEffects.h and UIImage+ImageEffects.m.
Also, I had to Enable Modules in my build settings because I had a project that wasn't created with xCode 5. To do this go to your target build settings and search for "modules" and make sure that "Enable Modules" and "Link Frameworks Automatically" are both set to yes or you'll have compiler errors with the new category.
Good luck blurring!
Check WWDC 2013 sample application "running with a snap".
The blurring is there implemented as a category.

Core Graphics memory leaks while displaying pdf images

I have a subclass of UIImageView view that loads pdf image data, so that I can have a resolution independent graphic in my view. Works well for the stated purpose, but I am getting memory leaks with this, according to an instruments leaks profile.
Here is the code below that I believe should be responsible for the leaks. I am trying to track down the problem, but I am a little foggy on how to pinpoint the issue.
- (id)initWithPDFResourceAtPath:(NSString *)path center:(CGPoint)center {
if ((self = [super init])){
CGPDFPageRelease(pageRef);
CGPDFDocumentRef documentRef = CGPDFDocumentCreateWithURL((__bridge CFURLRef)[NSURL fileURLWithPath:path]);
pageRef = CGPDFDocumentGetPage(documentRef, 1);
CGPDFPageRetain(pageRef);
CGPDFDocumentRelease(documentRef);
[self setBounds];
}
return self;
}
-(void)setBounds {
[self setBounds:CGRectApplyAffineTransform(CGPDFPageGetBoxRect(pageRef, kCGPDFMediaBox), CGAffineTransformMakeScale(scaleH, scaleV))];
size = self.bounds.size;
[self getPDFimage];
}
-(void)getPDFimage {
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextScaleCTM(context, scaleH, scaleV);
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
CGContextSetRenderingIntent(context, kCGRenderingIntentDefault);
CGContextDrawPDFPage(context, pageRef);
[self setImage:UIGraphicsGetImageFromCurrentImageContext()];
}
You forgot to call UIGraphicsEndImageContext(). Change your code to:
UIImage *image = [self setImage:UIGraphicsGetImageFromCurrentImageContext()];
UIGraphicsEndImageContext();
return image;
EDIT1: your code has this pageRef variable - is it an ivar or a static? If an ivar you better release it with CGPDFPageRelease() in the dealloc method. [It really should be an ivar]
EDIT2: See attached screen shot on Object Alloc. You can see the type and current amount and its ordered from most to least.
EDIT3: all else fails create a demo project that has the same problem and post it on Dropbox.
EDIT4: Code was uploaded to: here (I cannot look at it til May 28th)
EDIT5: The problem is that pageRef is not ever released. So:
1) remove this from your init method, as it does nothing:
CGPDFPageRelease(pageRef);
2 and move it to a new dealloc method:
- (void)dealloc
{
CGPDFPageRelease(pageRef);
}

Resources