I'm using a whole bunch of CALayers, creating a tile-based image not unlike GoogleMaps (different versions of the same image with more/less detail).
The code I'm using to do this is:
UIImage* image = [self loadImage:obj.fileName zoomLevel:obj.zoomLevel];
[CATransaction setValue:(id)kCFBooleanTrue
forKey:kCATransactionDisableActions];
obj.layerToAddTo.contents = [image CGImage];
[CATransaction commit];
I don't really feel like loading the CGImage from file using CoreGraphics because I'm lazy. But I will if there's a big performance boost! LoadImage just mangles a string to get the right path for loading said image, and obj is a NSObject-struct that holds all the info I need for this thread.
Help?
There's not a big performance boost - if anything it's the other way around. By going throuh UIImage to load up your images, you'll get all the benefits of caching that it does for you and it'll be a very speedy critter to use with your various CALayers.
I just tried this and using pure CoreGraphics to load the image rather than using UIImage gave a noticeable speed improvement when loading many images in one go.
"I just tried this and using pure CoreGraphics to load the image rather than using UIImage gave a noticeable speed improvement when loading many images in one go."
How did you avoid using a UIImage? Or more precisely, how do you load an image file directly into CoreGraphics without going through a UIImage?
One reason why NOT to use UIImage -imageNamed: to load images is that they are stored in the internal cache and that cache is not cleared in low memory situation.
I don't have a definite answer but I'd guess that you'd see a slower load time when using UIImage than you'd see when using CGImage. With CGImage, you specify the image type (jpg or png) during creation, but with UIImage, the object type needs to be determined dynamically. Admittedly, this is probably as simple as looking at the first few bytes of the image file, but it might not be.
Once the image is actually in use, I wouldn't imagine that there'd be any difference at all between using the CGImage that internally represents a UIImage vs. using a CGImage you created yourself. I'd think they'd be exactly equivalent.
Related
Environment:
I am creating a "photo mosaic" app, and I try to display 1024(32*32) pieces of small images(retina size->w:30px h:20px) on the screen same time. Which means on total, it is the same size as the full screen image size.
Issue:
I load 1024 UIImages, create 1024 UIImageViews, and add all of them to a UIView. When I scroll to this view, there is a big lag: test on iPhone4(iOS 5) and iPhone5(iOS 6). It's just appear on iPhone4, and on iPhone5 is fine. (Supposing iPhone5 have much more better CPU, so I think it is reasonable).
What I think:
Supposing all images have been already loaded from local dir in the memory(using method "imageNamed"), so I think the problem must be in the somewhere of the step display/render the images.
So any idea about it? Any, any idea will be helpful.
Thanks so much,
UPDATE
It is much better after I took the advice from #Antwan van Houdt . Here is the principle code:
-(void)updateCoverImageView:(UIImageView *)smallImage{
UIGraphicsBeginImageContextWithOptions(self.frame.size, NO, 0.0f);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[self.coverImageView.layer renderInContext:ctx];
[smallImage.image drawInRect:smallImage.frame];
self.coverImageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
Then you just set the alpha value of smallImage to zero, so the system won't render them. And the cover image will replace it. That works for the lag issue caused by displaying a large amount of UIView same time.
Is every image in its own view?
In either case, for such a huge amount of images you should probably consider drawing them in 1 view and if that drawing method is still too slow you could draw it once, render the view to an image and then simply draw that image as it's cached display.
There are several guides available from apple that deal with the performance of custom view drawing and stuff like it.. you should give them a read.
Not knowing the full details of your code, any thing like this, should be done on a background thread. I would read up on graphics contexts, grand central dispatch and then use both those concepts to create the resulting image in another thread and then get the resulting image and display it in the main thread.
Infact since you have many images which can all be processed in parallel, it can be done very fast using GCD.
If you can post the code, I can help you optimize it.
I'm trying to find ways to cut down memory usage for my app right now, and I have an idea regarding UIImages. I make pretty extensive usage of UIImages, some of them common to multiple views. Is it a lot more memory efficient to instantiate a single UIImage and then use a pointer to that same image throughout the app rather than allocating a new UIImage for the same image in each view? Or, is the OS smart enough to sort of automatically cache a UIImage so that it is only stored in memory once? (doubt that's the case but I have to ask)
Thanks.
Straight from UIImage apple reference docs for imageNamed:
This method looks in the system caches for an image object with the
specified name and returns that object if it exists. If a matching
image object is not already in the cache, this method loads the image
data from the specified file, caches it, and then returns the
resulting object.
You use a lot of images, so remember that [UIImage imageNamed:#""]; caches the images, so if you are having low memory problem use instead [UIImage imageWithContentsOfFile:#""]; that doesn't cache it. Maybe that can help you cut down memory usage.
When a view unloads set the GUI properties to nil in the viewDidUnload method of UIViewController subclasses.
Yes, it is more efficient to store a single UIImage and use pointers to it.
It is unlikely that the OS caches images in this way, and even if it did, using pointers gives you more fine control of when the image is released / deallocated so that you don't have to hope the OS is going to take care of it.
I am trying to figure out how I can shrink and save a UIImage, read it back in later, and then convert back to the original size without losing quality. I have managed to do the resizing and saving, but the problem is that if I save it smaller, when I read it back in and expand it, the quality is very poor. Does anyone know how this can be done without losing image quality?
You can't downsize the image and then bring it back without losing quality. You can't make something out of nothing, once you lose the data you lose the data.
You will need to save two versions of the image, one large and one small. This is a very typical scenario when dealing with thumbnails.
Check out the following site which provides categories for resizing images as well as several other really cool stuff:
http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/
Although you cannot upsize a downsized image, you can display a downsized one while retaining a reference to the original image (which you can save):
UIImage *image = [UIImage imageNamed:#"myImage"];
[image drawInRect: destinationRect];
UIImage *thumbnail = UIGraphicsGetImageFromCurrentImageContext();
UIImageWriteToSavedPhotosAlbum(image,nil,nil,nil);
The destinationRect will be sized according to the dimensions of the downsized version.
I'm trying to improve scrolling performance on a UITableView that uses cells with images fetched from the web, but stored in the NSCachesDirectory. The cells have a custom content view to draw the contents (an image).
When I use a placeholder image from the app bundle, using [UIImage imageNamed:#"Placeholder.png"], scrolling performance is super fast.
When I load an image from the disk cache (NSCachesDirectory) using [UIImage imageWithContentsOfFile:cachePath], scrolling performance gets worse.
According to the documentation, imageNamed: caches the image and imageWithContentsOfFile: does not.
How to use UIImage's system cache when using imageWithContentsOfFile: ?
Thanks a bunch!
It seems to be possible to use the path to an image in the NSCachesDirectory as argument for the [UIImage imageNamed:] method. The method accepts relative paths (relative to the app bundle), e.g.: #"../Library/Caches/SomeCachedImage.png" works.
UIImage automatically caches the image in memory if it is used multiple times, which improves the performance when an image is used multiple times in a table view.
The problem is most likely that you are loading and decompressing the image in the main run loop. This will block the user interface for a short time. You get much better performance if you do the loading and decompression in a seperate thread and only set the image in the main loop. (Which is also required for user interface changes, which setting an image on a UIImageView is)
This will require some more infrastructure. Like for example a notification scheme or key value observing.
You can't. imageWithContentsOfFile: will always load the image from file (though lazily). What you can do is create an in memory cache of you own with NSArrays or NSDictionaries, depending on how you'll want to do the lookup.
I need to use a stretchable UIImage hundreds of times in my app in multiple UIImageViews. Is it okay to globally reuse the same stretchable UIImage instead of having to recreate them in memory each time I need to add it to a UIImageView?
I know [UIImage imageNamed:] caches images for better performance, but this cannot be used for stretchable images!
Thanks,
Mike
Oh, absolutely, you should always store your image once and then call that same image every time you want to draw it. Typically for projects with a lot of images I have a single SpriteManager class that contains an NSDictionary full of sprites. Then I can reference each one via filename. This way each image is loaded only once, which is significantly faster.
The absolutely most efficient way to do this, though, is just to use OpenGL ES. And if you've got hundreds of images being drawn, I absolutely recommend that you do so.
Definitely: Reuse your image. Are you storing it in a global variable? Make sure you retain the image (or put it in a retained property fo some global class like your Application) since it probably came AutoReleased.