Huge PNG images in IOS - iphone

I have a lot of png images in my app which is causing my app to overload the real memory usage of my iPad2 device. My whole app folder with lots of sound files and png images is only about 50-60 MB precompiled, 90 MB on device, but still I'm easily going up to 300MB++ at run time.. ViewControllers on top of former ViewControllers etc.. which I'm also trying to fix..
What I find strange is that by just displaying one background .png image I'm adding 12 MB onto real memory usage(seen in instrument). The image that I used to fill a ImageView image in the storyboard is only 700 KB in my project folder. Taking it out or leaving the image field empty saves me 12 MB of memory...
I'm using a lot of these background images as well as other foreground images in the app which is eating up way to much space.
Any suggestions or help is appreciated
Thanks.

Well, 700kb image on disk space doesn't mean 700kb image in memory. It is compressed while stored on disk, but when it is taken into memory - it will grow in size.
If you are using a lot of images in your project, I would recommend using [UIImage imageWithContentsOfFile:] method. It doesn't cache images internally and you have more control over the memory than using [UIImage imageNamed:].
For me, the general rule of thumb is this. If the image is huge and used once in the app -> [UIImage imageWithContentsOfFile:], but if the image is reused in many places over the app -> [UIImage imageNamed:].
In addition, if you have to use .png format because it has transparency, then you might try giving .webp a chance. This format is not supported officially in ios, however there is a custom implementation on github you can take a loot at.
UPDATE:
I personally don't use interface builder in my apps at all, as I find it extremely time consuming and slow. Instead I create all views programmatically, that gives me more flexibility like choosing between [UIImage imageWithContentsOfFile:] or [UIImage imageNamed:]. You can just set an Outlet to your UIImageView and then set the actual image in code.
As for pngs, there is no such thing as the preferred type of images in iOS. It really depends on your case. If you need transparency -> png, need just a plane image -> jpg. This is just a simple example.
And as for .webp, this format, as I have already mentioned, is not officially supported in iOS, but you can add your own support for it. Basically, .webp will let you replace .png and reduce the size of project folder without loosing transparency in your images.
Hope this helps, cheers!

Related

How to load a large local image (works on simulator, not test device)?

I'm currently trying to load a map image which is a large(16mb) .jpg file inside a scrollView so you can zoom in and out.
When I launch the app inside the simulator everything runs fine and smooth. However once I run it on my test device (iPod 4.1, iOS 6.0) the app shows the launch image and then it crashes with no error messages at all.
This is how the code currently looks like.
myImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"map.jpg"]];
myScrollView=[[UIScrollView alloc]initWithFrame:CGRectMake(15,120,290,365)];
myScrollView.showsVerticalScrollIndicator=YES;
myScrollView.scrollEnabled=YES;
myScrollView.maximumZoomScale = 10.0;
myScrollView.minimumZoomScale = [myScrollView frame].size.width / myImage.frame.size.width;
myScrollView.clipsToBounds = YES;
myScrollView.bounces = NO;
myScrollView.showsHorizontalScrollIndicator = NO;
myScrollView.showsVerticalScrollIndicator = NO;
[self.view addSubview:whiteFrame];
[self.view addSubview:myScrollView];
myScrollView.contentSize = CGSizeMake(myImage.frame.size.width,myImage.frame.size.height);
myScrollView.delegate = self;
myScrollView.zoomScale = [myScrollView frame].size.width / myImage.frame.size.width;
[myScrollView addSubview:myImage];
Any help is greatly appreciated.
EDIT:
I found this in the docs
If you have a very large image, or are loading image data over the web, you may want to create an incremental image source so that you can draw the image data as you accumulate it. You need to perform the following tasks to load an image incrementally from a CFData object:
Create the CFData object for accumulating the image data.
Create an incremental image source by calling the function CGImageSourceCreateIncremental.
Add image data to the CFData object.
Call the function CGImageSourceUpdateData, passing the CFData object and a Boolean value (bool data type) that specifies whether the data parameter contains the entire image, or just partial image data. In any case, the data parameter must contain all the image file data accumulated up to that point.
If you have accumulated enough image data, create an image by calling CGImageSourceCreateImageAtIndex, draw the partial image, and then release it.
Check to see if you have all the data for an image by calling the function CGImageSourceGetStatusAtIndex. If the image is complete, this function returns kCGImageStatusComplete. If the image is not complete, repeat steps 3 and 4 until it is.
Release the incremental image source.
Does anyone know about a sample code for that ?
A 16mb jpg is going to take a pretty decent chunk of RAM when uncompressed. Your app is probably crashing because it is using too much memory. You can check this by reproducing the crash and checking the device's console log in the Xcode organizer.
You will need to either:
Reduce the dimensions of the image as much as your design will permit to greatly reduce memory usage. For example, there's no reason to have a 16mb jpg if it will never be shown at full-resolution.
Chop the image up into tiles and only load the tiles currently displayed on the screen and the surrounding areas. Then load additional tiles as the user scrolls around. This is how maps apps are able to display extremely large images without running out of RAM.
Remember to also test your app on the supported device with the lowest amount of RAM. These devices will probably kill your app sooner than the newest devices.
At 16 MB, the image is too big for your iPod to handle. The app is crashing because it's loading the image directly, and asking for too much memory and the system has had to kill it.
You should create a smaller version of your image, in terms of both canvas size and image quality, for your app.
You could also incrementally load the image instead.
When you're testing on the simulator, it has full access to the gobs of memory on your computer. Way more than the 256MB available to your iPod 4 (there isn't such thing as an iPod 4.1).
Have a look at CATiledLayer
https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CATiledLayer_class/Introduction/Introduction.html
There is also an example Apple project using it
https://developer.apple.com/library/ios/samplecode/PhotoScroller/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010080
Good luck
I hope you are not loading this image on app launch. Application launch should be as light as possible. If launch time is more than 20 secs then iOS kills the app on its own. On simulator resources are much bigger and you may not see the similar behavior as that with device. You may also want to use the lazy loading technique to load lighter image first and then load heavy image only when needed while zooming, panning etc.
Having said that, one imortant aspect of [UIImage imageNamed] is that it caches all images loaded in that way and they never get unloaded, even if you dealloc the UIImage that was created! This is good in some circumstances (e.g. smallish images in UITableView cells), but bad in others (e.g. large images).
The solution is to use [UIImage imageWithData] which does not do this caching and which unloads the data when the UIImage is dealloc'd.
Another solution to this problem consists of either loading scaled version of the image (*1) or you have option to display it in full resolution with help of CATiledLayer(*2).
[UIImage imageWithCGImage:scale:orientation:];
CATiledLayer example

Decompress images to use on a UITableView

I currently working on a project that i need to display large images on a UITableView,this is very common problem for a lot of developers and learning with they threads i reached to the following procedure:
NOTE:The large images i refer,they all have 300x300px(600x600px,retina) and about 200kb,JPEG
Create a NSOperationQueue;
Download images asynchronously(Each image has 600x600px,corresponding to the #2x image);
Resize and create the non retina image(300x300px image);
Decompress both images;
Store all images on a NSCache;
After all that procedures have finished i update my UITableView on the main thread.I'am using a UITableViewCell subclass to draw all my needed content(As seen on Apple's sample codes).The main problem is that i'm confused about step 4(decompress images),my doubts:
NOTE:I'm currently storing my decompressed images on a NSCache.
Should i decompress the images and store then as UIImage's or NSData's?
How can i store the decompressed images?(NSCache,NSMutableArray...)
What is the best way to pass the decompressed image to my UITableViewCell subclass?
NOTE:I'm using the decompression code presented here:link
You can't really store an UIImage object to disk, but you can turn it to NSData using UIImagePNGRepresentation
Using UIImage will give you cache out of the box, I bet it's the most efficient you can get
Just put the image into UIImageView, Apple spent a lot of time on making image rendering fast.
That said, your images are not particularly big, especially for retina devices, I would advice looking at something like AFNetworking library that has a complete and tested solution for this problem.
Plus, you can look up the code of AFImageRequestOperation which does exactly what you need: download, store, cache, reuse.

Reduce app size cocs2d iOS

I am new with cocs2d. I have created an app using Cocos2d. The app is working fine but problem is that application size is too large 350MB. There are many images in this app. I have used png and where possible jpeg images. There are many png images that have larger than 1 mb.
Is there any way to reduce the application size. I've reduced the size whereever possible. Is there any other format that can be used in place of png? There are no many animations. The png are used only purpose of transparency.
Your images are way out of size. Even if you put them on server, and then download it will take time to download.
The best option is to reduce the image size. A couple of sites that can help you do that are:-
Reduce image size
Compress image size
You can keep the image content on your server and then download the image content asynchronously (which is more imp download it first). If there are levels then download initial levels first and download remaining on the background thread. You can always display a loader on launch and display some help kinda stuff meanwhile the data gets download and cached.
You can make use of SDWebImage and other libraries to get your images stuff async.
Hope it helps.
While your pictures are fairly large and you should try to reduce the number and size, you can make gains through packaging the .png into pvr.ccz files. There are multiple different programs available to do this. I like to use Texture Packer which is available here: http://www.codeandweb.com/texturepacker
You can find some tips in my post on reducing memory usage & bundle size.
Most importantly use texture atlases in .pvr.ccz format and where possible reduce image color depth to 16 bit. Avoid JPGs altogether because they're terribly slow to load in cocos2d.
There is no issue in using png files although your images are too large, You can reduce their size by 70 - 80% by using tinypng and it will not going to hurt your graphics.
https://tinypng.com/
I usually edit the image size now https://resizeimage.io , you try!

What's the fastest way to load big image on iPhone?

Should I use UIImage or CGImage ? Png or Jpg ?
I've read the doc and tried different things but did not notice significant improvement.
Loading an image can take 1 good second which seems slow
UIImage is just an ObjC wrapper of CGImage, so they're the same.
From the SDK doc:
You should avoid creating UIImage objects that are greater than 1024 x 1024 in size. Besides the large amount of memory such an image would consume, you may run into problems when using the image as a texture in OpenGL ES or when drawing the image to a view or layer. This size restriction does not apply if you are performing code-based manipulations, such as resizing an image larger than 1024 x 1024 pixels by drawing it to a bitmap-backed graphics context. In fact, you may need to resize an image in this manner (or break it into several smaller images) in order to draw it to one of your views.
If you have a huge image, you could try to use a UIWebView to reduce memory consumption.
The time to load an image has 2 parts: the time to decompress the image (relevant to choosing JPG or PNG) and the time to render the image.
For decompressing, I'd suggest you profile the simple statement
[UIImage imageWithContentsOfFile:#"/path/to/your/image.jpg"];
It is faster for the iPhone to load PNGs than JPGs because PNGs are optimized when bundled in your application (although, not loaded from remote).
An except from Addison Wesley's iPhone Cookbook:
"Xcode automatically optimizes your PNG images using the pngcrush utility shipped with the SDK. (You'll find the program in the iPhoneOS platform folders in /Developer. Run it from the command line with the –iphoneswitch to convert standard PNG files to iPhone- formatted ones.) For this reason, use PNG images in your iPhone apps where possible as your preferred image format."
Also, PNG is a lossless format, and JPGs are lossy. Apple chose this format for these reasons.
-Kevin

Performing iPhone optimization on externally downloaded PNGs

When a PNG is added to an XCode iPhone project, the compiler optimizes it using pngcrush. Once on the device, the image's rendering performance is very fast.
My problem is that my application downloads its PNGs from an external source at runtime (from Picasa Web albums, using the Google Data APIs). Unfortunately, these images' performance is quite bad. When I do custom rendering on top of the image, it seems 100x slower than its internally stored counterparts. I strongly suspect this is because the downloaded images haven't been optimized.
Does anyone know how I can optimize an externally downloaded PNG at runtime on the iPhone? I'm hoping for a class that does this. I even considered adding pngcrush's source code to my app, which seems drastic. I haven't been able to find an decent answer myself. I'd be very grateful for any help.
Thanks!
Update:
Some folks have suggested that it may be due to the file's size, but it isn't. During my tests, I added a toggle button to switch between using the embedded version and the downloaded version of exactly the same PNG. The only difference is that the embedded one was optimized by 'pngcrush' during compilation. This does some byte-swapping (from RGBA to BRGA) and pre-multiplication of alpha. (http://iphonedevelopment.blogspot.com/2008/10/iphone-optimized-pngs.html)
Also, the performance I'm referring to isn't the downloading, but the rendering. I superimpose custom painting on top of the image (overriding the drawRect method of the UIView), and it's very choppy when the background is the downloaded version, and very smooth when it's the embedded (and therefore optimized) version. Again, it's exactly the same file. The only difference is the optimization, which I'm hoping I can perform on the image at runtime, on the device, after downloading it.
Thanks again for everyone's help!
That link you posted pretty much answers your question.
During the build process XCode pre-processes your png so it's in a format that's more friendly to the graphics chip in the iPhone.
Png's that have not been processed like this will likely use a slower rendering path, one that deals with the non-native format and the fact that the alpha must be computed separately for each color.
So you have two options;
Perform the same work that pngcrush does and swap ordering/pre-multiply alpha. The speed up may be due to one or both of these.
After you have loaded your image, you can "create" a new image from it. This new image should be in the iPhone's native format and so should perform faster. The downside is it could potentially take up a bit more memory.
E.g.
CGRect area = CGRectMake(0, 0, width, height);
CGSize size = area.size;
UIGraphicsBeginImageContext(size);
[oldImage drawInRect:area];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
The fact that you say it "seems" 100x slower indicates that you have not performed any experimentation, but made a guess (it must be the PNG optimization), and are now going down a path based on a hunch.
You should spend time to confirm what the problem is before you try to solve it. My gut says that PNG optimization shouldn't be the issue: that mostly affects the loading of images, but once they are in memory it doesn't matter what file format they were originally in.
Anyway, you should try an A-B comparison, either get your code to load an optimized PNG from somewhere else and see how it compares, or make a test app that just does some drawing on the two PNG types. Once you've confirmed what the problem is, then you can figure out if you need to compile pngcrush into your app.
On the surface, it sounds like something else is at play here. Any additional image manipulation should only add time until it's displayed onscreen...
Would it be at all possible to get the server to gzip the images by sending the appropriate HTTP header? (If it even helps file size much, that is.)
Temporarily using the pngcrush source might be a good test as well, just to get some measurements.
Are you storing the png at the original downloaded size? If it's a large image it'll take significantly longer to render.
Well it seems that a good way to do it (since you can't run pngcrush on the iPhone and expect that to speed it up) would be to make your requests through a proxy that runs pngcrush. The proxy would have nice horse power to actually give you some gain over the 100x pain you feel.
try pincrush to trans the normal png file to the crushed png file
You say you are drawing on top of the image by overriding a UIView's drawRect: method. Are you trying to do some animation by repeatedly drawing the whole image with your custom stuff on top of it?
You might get better results if you put your custom stuff in a separate view or layer, and let the OS deal with compositing the result over the background. The OS will only update the parts of the screen that you actually change, and won't be repainting the entire image as often.