2 MB of GIF images consuming 8 MB of memory - ios

I am creating a Keyboard Extensions, which shows animated GIF images, at a time it shows only max 2MB of images distributed in 20 GIFs all together.
Whenever the GIFs are loaded, the memory consumption increases by more than 8MB, which is causing Memory Pressure, and the keyboard is getting terminated as soon as it is loaded.
I am using SDWebImage to show the GIFs.
Can anyone suggest me, how should I use GIFs, or is it good idea to show animated GIFs in keyboard extensions? Why is the extension taking up so much memory?
Thank You.

SDWebImage has a lot of features so the object would be very heavy. Try to use FLAnimatedImage. It is made specifically for showing GIFs

Try to use FLAnimatedImage with Nuke which automatically integrates FLAnimatedImage for you. FLAnimatedImage is the best in class animated GIF engine for iOS at the moment.
The problem with SDWebImage is that they have their own implementation of animated GIFs. They iterate thought all the frames and create bitmaps upfront:
for (size_t i = 0; i < count; i++) {
CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL);
duration += [self sd_frameDurationAtIndex:i source:source];
[images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]];
CGImageRelease(image);
}
FLAnimatedImage on the other hand creates bitmaps on-demand and intelligently caches them.

When the GIFs are loaded they will be decompressed so they will take a lot more space in memory. This is because iOS needs the images in raw bitmap format in order to edit or display them.
The GIF format has a very good compression ratio with the limitation of having only a 8bpp color space. So a 4x compression in your case is very likely.

Related

ScreenShot is too large when taken in high resolution iOS devices

When I take a screenshot for a full screen size view in high resolution iOS devices, the result image data is very large. For example, the resolution of iPhoneX is 812*375, and screen scale is 3. Thus, a ARGB image for a full screenshot will take about 812*3 * 375*3 * 4 bytes, aka 10.4MB. So when I use these screenshot images in my app, the memory usage will jump to a high level, and maybe trigger a memory warning.
Here is my code:
if (CGRectIsEmpty(self.bounds)) {
return nil;
}
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [[UIScreen mainScreen] scale]);
[self drawViewHierarchyInRect:self.bounds
afterScreenUpdates:NO];
UIImage *renderImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Even if I compress the screenshot image, there is still some pulses in memory usages.
So my question is: Is there any good way to take a high resolution screenshot and avoid memory pressure?
I faced the same problems while working with images - memory usage can be extreme causing memory warnings and crashes, especially if using UIImageJPEGRepresentation method on older devices (iPhone 4). So I tried to avoid using this method by saving pictures to Gallery and fetching them afterwards, this does not help much though, memory jumps persist anyway.
I suppose "pulses" are caused by copying the whole data to memory during converting. Possible solution would be to implement custom disk caching and decoding mechanism so the data could be processed in chunks, but still don't know if it worth to do. For me this problem still persists, maybe following list could be helpful.
Also refer to this question.
Other solution is to free view controllers resources in didReceiveMemoryWarning method if possible.

Loading large images faster in UIImageView

Is there an accepted way, or updated way, to more quickly load images into a UIImageView?
My scenario: A collection view, with a large UIImageView. Only 1 cell is displayed at a time. I have implemented NSCache and Prefetching on the collection view already. Performance on scrolling has a pause partway through. The images I am using are "relatively" large, in order to accomodate both an iPad and iPhone layout. For example, images are 1600x1600px, RGB, PNG. (from 2-5MB compressed, ~10MB uncompressed, stored locally in the app)
Once the images are loaded, scrolling back and forth is usually OK then, ~60fps visually. But on first load they are ALWAYS jittery. BUT if I make the images physically smaller, such as 800x800 then they load quickly and I can not see a jitter on scroll. So I am dealing with an image size vs drawing speed issue. Same issue seen on a 5s as on an iPhone X.
The same performance hit happens with [UIImage imageNamed:imageName] or [UIImage imageWithContentsOfFile:imagePath]
I am reading how UIImages are decompressed before actually being drawn, and if the system has to draw a subsampled image, it can significantly affect main thread performance. I've done a little Instruments testing and confirmed that it appear that none of my code is actually slow, the image drawing of a PNG is slow.
Is there a newer way to do something similar to the content in links below, IE draw an image in a CGContext and hope it stays cached?
https://www.cocoanetics.com/2011/10/avoiding-image-decompression-sickness/
- (void)decompressImage:(UIImage *)image
{
UIGraphicsBeginImageContext(CGSizeMake(1, 1));
[image drawAtPoint:CGPointZero];
UIGraphicsEndImageContext();
}
https://gist.github.com/steipete/1144242
this seems like real overkill for me:
https://github.com/path/FastImageCache
I have implemented NSCache and Prefetching on the collection view already.
Good, but
(1) you should not NSCache images
(2) you should downsize the image to the max size needed for display
(3) you should not be doing anything time-consuming in itemForRowAt: (you didn't show yours) — you have only a couple of milliseconds to produce the cell and get out
(4) if you can't provide the image in time, provide a placeholder and get out of itemForRowAt:; you can always reload later when you have the real image
(5) do all time-consuming work off the main thread (includes converting to UIImage and drawing UIImage to downsize it)
(6) measure measure measure! this is why we have Instruments; do not guess where the problem is

Large jpeg using more memory than smaller jpeg when loaded in a UIImageView. Why?

The login view in our app uses large background images. To try and save on memory/app size I resized and compressed these images, which reduced their filesize significantly (less than 1mb, down from several mb).
When monitoring my apps memory usage (XCode debugger) there is a clear spike when a modified image is displayed (around 30-40mb). I'd accepted this as normal and simply made sure to release the image asap to limit memory usage.
I've recently started replacing a couple of the images and wanted to preview the new ones before resizing/compressing them. I noticed that these images (one of which is 11mb on disk and 4640x3472 pixels) has no visible effect on app memory usage whatsoever, increasing 'Other Processes' instead (by around 20-30mb).
Can anyone explain what's happening here? I want to confirm it is advisable to continue resizing/compressing the images.
Note that I'm loading the images using UIImage(contentsOfFile:) and I resized/compressed the images using GIMP. The new images have been taken straight from Flickr and unmodified.
Cheers.
The in-memory size of the image (as a UIImage) is different to the compressed on-disk size (your JPEG)
The UIImage takes 4 bytes (RGBA) per pixel x height x with - so for a 4640 x 3472 image, you're looking at 64,440,320 bytes - quite different to the 11MB on disk

Preloading images ios app

I have an app with 150 local images (about 500kb each). I have loaded them all into an array like this:
allPics = [[NSMutableArray alloc] init];
//NSString *imagePath;
NSArray *result = [database performQuery:#"SELECT image_path FROM validWords order by valid_word"];
for (NSArray *row in result) {
NSString *temp = [row objectAtIndex:0];
NSLog(#"%#", temp);
//imagePath = temp;
UIImage *newImage = [UIImage imageNamed:temp];
[allPics addObject:newImage];
}
When I set my UIImageView later to one of these pics, it hangs my interface up for a second, due to lazy loading from what I have read. I tried to prerender them, but that spiked my memory usage to over 3gb before it got a third of the way through my images. Should I be looking to use a background thread to render the image when I need it? When I reduced the image total to 4, once all 4 were rendered once, the transitions between them was seamless.
I appreciate any and all tips and solutions!
Yes, I would suggest a background thread and paging. If the user is looking at image 7, you should load images, say, 5,6,8 and 9. If the user then moves onto image 8, you can discard image 5 and lazy load image 10. This way the user sjhould be able to move through your images without a significant memory or performance overhead.
You can then also add heuristics such as 'if the user is paging through the images very quickly, don't load any images until they slow down',
Another tip is to store a very low resolution version of the image (say, a 50kb version) and store that at a different path. Then you can show the thumbnail images to the user and only lazy load in the high res image if the user stops on that image for a period of time.
Finally, be careful when you talk about image sizes. Is the 500KB compressed or uncompressed? If it is a 500KB compressed JPeg, the actual image on the device could be vastly bigger. A jpg with fairly uniform colour and a reasonably high level of compression can be very small on disk, but decompressed it could be a massive image. This could be another source of the lag you experience.

iOS faster way to update UIView with series of JPEGs ie MJPEG. (Instruments shows 50% CPU)

I'm receiving a series of JPEGs over the network from a camera (MJPEG). I display the images as I receive them in a UIView. What I'm seeing is that my App is spending 50% of CPU (device and simulator tested) in what appears to me to be the UIView update.
Is there is a less CPU intensive way to do this screen update? Should I process the JPEG in some way before handing it over to UIView?
Receive method:
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(),^{
[cameraView updateVideoImage:image];
});
Update method:
- (void) updateVideoImage:(UIImage*)image {
myUIView.image = image;
...
update: added better screen capture
update2: Is OpenGL going to provide a quicker surface to render to for JPEG? It's not clear to me from Instruments where the time is being spent, render or decode. I'm going to put together a test case as suggested and work from there.
iOS is optimized for PNG images. While JPEG greatly reduces the size of images for transmission, it is a much more complex format, so it does not surprise me that this rendering is taking a lot of time. People have said there is jpeg hardware assist on the device, but I do not know for sure and even if its there it maybe tuned for certain image types.
So - some suggestions. Devise a test where you take one jpeg you have now, and render it to a context, and baseline this time. Take the same image and open it in Preview, then save it with a slightly different quality value to another file, and try that (Preview will strip out unnecessary "junk" from the image, or even convert it first to a png then back to a jpeg. The idea here is to use an image output from Preview, which is going to be as clean an image as you are going to get. Is the image any better?
You can also try using libjpegturbo, and see if it can render your images faster. You can see that library in action in a github project, PhotoScrollerNetwork. You may find that project of use as it decodes the jpegs (using that library) in real time as they are received, and then supports zoomable viewing using CATiledLayers.

Resources