Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIImage decompression causing scrolling lag

I have this app with a full screen tableView that displays a bunch of tiny images. Those images are pulled from the web, processed on a background thread, and then saved to disk using something like:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{     UIGraphicsBeginImageContextWithOptions(rect.size, YES, 0);     // code that adds some glosses, shadows, etc      UIImage *output = UIGraphicsGetImageFromCurrentImageContext();      NSData* cacheData = UIImagePNGRepresentation(output);     [cacheData writeToFile:thumbPath atomically:YES];      dispatch_async(dispatch_get_main_queue(), ^{         self.image = output; // refreshes the cell using KVO     }); }); 

This code is only executed the first time the cell is displayed (since after that the image is already on disk). In that case, the cell is loaded using:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{     UIImage *savedImage = [UIImage imageWithContentsOfFile:thumbPath];      if(savedImage) {         dispatch_async(dispatch_get_main_queue(), ^{             self.image = savedImage; // refreshes the cell using KVO         });     } }); 

My problem is that in the first case, scrolling is butter smooth. But in the 2nd case (where it's reading the image directly from disk), scrolling is super jerky, even once the image is loaded. Drawing is what's causing the lag. Using Instruments, I see copyImageBlockSetPNG, png_read_now and inflate are taking up most of the cpu (they aren't when assigning self.image to UIGraphicsGetImageFromCurrentImageContext())

I'm assuming this happens because in the first case the UIImage is a raw output of the drawing, whereas in the second case it has to decompress the PNG every time it's drawing it. I tried using JPGs instead of PNGs and I get similar results.

Is there a way to fix this? Maybe to have it only decompress the PNG the first time it gets drawn?

like image 715
samvermette Avatar asked Apr 13 '12 22:04

samvermette


1 Answers

Your problem is that +imageWithContentsOfFile: is cached and lazy loading. If you want to do something like this, instead use this code on your background queue:

// Assuming ARC NSData* imageFileData = [[NSData alloc] initWithContentsOfFile:thumbPath]; UIImage* savedImage = [[UIImage alloc] initWithData:imageFileData];  // Dispatch back to main queue and set image... 

Now, with this code, the actual decompression of the image data will still be lazy and cost a little bit, but not nearly as much as the file access hit you're getting with the lazy loading in your code example.

Since you're still seeing a performance issue, you can also force UIImage to decompress the image on the background thread:

// Still on background, before dispatching to main UIGraphicsBeginImageContext(CGSizeMake(100, 100)); // this isn't that important since you just want UIImage to decompress the image data before switching back to main thread [savedImage drawAtPoint:CGPointZero]; UIGraphicsEndImageContext();  // dispatch back to main thread... 
like image 192
Jason Coco Avatar answered Sep 24 '22 00:09

Jason Coco