ScreenShot is too large when taken in high resolution iOS devices - ios

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.

Related

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

iOS app crashes because images use too much ram

I know this is a stupid problem, but this is my first real app that I have to make, I have no one to ask and I looked up this problem and found no other similar problems.
My app crashes on real devices with no exception. I saw in the simulator that uses too much RAM and after a while I got to the conclusion that the pictures I am using are to blame.
The app is structured in this way: it has 8 viewControllers for different things: for example, it starts with one which lets the user select the avatar with which he/she will play and here I have two pictures, next is a viewController which shows the stats for that avatar and here it is another picture and so on. The problem is that each picture uses 40MB of RAM to be displayed and things add up so the app uses more than 300MB of RAM when the user gets to the gameviewCOntroller where the game is. Because of this, on devices like iPAD 2 or iphone 4 it crashes, but not on iphone 5.
I tried to set the images both from "images.xcassets" and from a ".atlas" folder, but the result is exactly the same. The pictures have a dimension of no more than 1500x1999px, they are in png format.
Also, I saw that if the app were to start directly into the gaveViewController it would use 180MB so the other viewController remain in memory or something like that. Should I "clear" them or something similar?
//-------update-------
This is what I got from Instruments:
Memory is a big deal on mobile devices, there is not a clear answer to you question, but I can give you some advices:
If your images are plain colors or have symmetric axes use resizable images. You can just use one line of pixel multiplied by with or height to cover the entire screen using a small amount of memory
Image compression doens't have effects when the image is decompressed. So if you have a png that is 600kb and you are thinking that converting in a 300kb will lower memory usage is only true for "disk space" when an image is decompressed in memory the size is widthXheightXNumber_of_channelXbit_for_channel
resize images: if are loading a 2000px square image into memory and you show it inside an image view of 800 px square, resize before adding it.You will have just a peak while resizing, but later it will use less memory
If you need to use big images, use tiling techniques such as CATiledLayer
If you don't need an image anymore get rid of it. It's ok to have an array of path to images, but not an array of full uncompressed images
Avoid -imageNamed it caches images and even if Apple says that this cache is released under memory pressure, you don't have a lot of control on it and it could be too late to avoid a crash
Those are general advices, it's up to you if they fit your requirements.
You should definitely follow Andrea's advices.
Additionally you should consider setting the image size to exactly what your need is. You're saying that you've tried to set them from xcassets so you have full control over the images you're loading, which is great (compared to downloading an image that you cannot modify).
I highly suggest you read some documentation on using Asset catalog files. This will allow you to have high-resolution image for bigger screens that also have more memory, and smaller ones for older devices, which is what you want here.
Also, note that 1500x1999px is still a very big size for most mobile devices.
More links about screen-size:
https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/IconMatrix.html
http://www.paintcodeapp.com/news/iphone-6-screens-demystified

Is it the "weight" or height and width that matters

I'm developing a game with lots of graphics for iOS platform. None of the graphics are dynamic, they're all ready made images. There are 6 layers on the screen and each layer contains 3-4 graphics objects in average that constantly scroll to left, each with different speeds. So with each screen refresh about 20-25 objects are scrolled, removed from screen and added again from right. The game is universal so images of all sizes present in the resources folder.On iPads, iPhone 5, iPhone 5s it goes smooth. But on 4th Gen iPod touch I notice some hiccup.
When I test the app with Instruments, I notice a critic memory problem at start and then the problem goes away. So this is all because I load all the above graphics on application start. All the images are combined into 4 different sprite sheets. So here's my question:
Is it the weight in kilobytes that matters or is it the dimensions? I'm asking this because I reduced the size of the images by about 70-80 percents, though their height and width remain the same, but that memory problem still exists.
If you are reducing the size of the images by utilizing compression, then expect there to be delay and a memory hit as the images are uncompressed when first loaded, and they will take up as much memory as they did before compression.
The 4th generation iPod Touch probably is having some memory pressures and it will try to release memory elsewhere in order to allow for your images to load up and stay in memory. That may be the hiccup you see, as other apps, like Mobile Safari or Mail are asked to give up some memory or to terminate.
(The size the images take in the resources folder is inconsequential. It is the size they take when they are uncompressed and in memory that matters).
You could make the images smaller by making them 4bit instead of 8bit color, or you could make them monochrome. You can also use a non-retina size or smaller, and then let the OS stretch them out to fit the space required. I would try to use the best images first, and if you get a memory warning (didReceiveMemoryWarning), then reload with the images that are less dense.
you can not load all image before the game beginning, you should use the cache to control the size of the memory. And before the scenery of game change ,the release the cache ,reload the need ones.
So you must control your resource dependents in the every game scenery.
This is the common method to management the large resource application.

High memory usage in the app

I have an app that has 12 images contained in an array. All these images are displayed at the same time on the screen. (1 view - 12 much wider and higher images (UIImageView's) one on another. When user does something, app moves the images, thus the view displays different scenes)
The images themselves are not too heavy (it is about 2500x5000 in size, but the whole folder with images is around 3.5 MB).
After loading, the app consumes 355 MB.
When I put breakpoint in viewDidLoad (and all images are loaded at that time), xcode shows that the app consumes only 9 MB, but in viewDidAppear it is 355 MB.
What is the reason of it? And how can I store images compactly? (As I assume that the problem is in the images).
Thank you for any help!
An image open will occupy something like : H x W x number of channel x number of bit for channel, the file size is another thing, because images are compressed according to their type. Your images are 50Mb each one in memory.
The only way is to resize the image before displaing them. There are plenty of image resizing categories online, just google a little bit.
The other suggestion is to not load all the images togheter, just bring in the array the file path, and instantiate the image lazily.
If you need to use hires images you should look for CATiledLayer and tiling techniques.
These images probably cannot be displayed all at the same time onscreen, so you may want to load them only when necessary.
If you still need to display them all together onscreen, think about reducing their size.
Still, 355Mb is huge. Are you doing anything else in that application, that might use up all of that memory?
In Xcode go to Product->Profile. You will find there a lot of useful instruments which will help you to find problems with memory, CPU or battery usage. However, check it if you haven't seen it before.

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