Most efficent way to create a CALayer with an image in it? - iphone

I am wondering what the most efficient way is to make a CALayer with an image in it.
I know you can load a UIImage and then call [image CGImage] but is this the best way of going about it? As far as I can tell from Apple's documentation this is the only way you can do it.

Try this:
layer.contents = (__bridge id)[ uiimage CGImage ] ;
HTH

Well, a CGImage isn't a CALayer, so you are obviously leaving some steps out there, but I assume you know what you're talking about, as far as drawing to a CALayer or whatnot.
If your question is about creating CGImages without using a UIImage, you can try looking into the following functions:
CGImageCreateWithJPEGDataProvider
CGImageCreateWithPNGDataProvider
If you happen to know beforehand what sort of image it is, just use the appropriate method. Otherwise, you'd need to look at the file signature to see if it contains PNG or JFIF. Of course, this requires you implement a CGDataProvider.
I assume this is exactly what the UIImage class is already doing. If you want to squeeze out every bit of efficiency from that, you can use one of the following methods in the UIImage class:
+ (UIImage *)imageWithContentsOfFile:(NSString *)path
+ (UIImage *)imageWithData:(NSData *)data
The only difference between these methods and
+ (UIImage *)imageNamed:(NSString *)name
is that imageNamed: caches the image. If you use one of the first methods, it does not.

Related

-[UIImage CGImage] returning nil

I'm wondering under which circumstances this code breaks in the second assert. In other words, when can -[UIImage CGImage] return nil? The documentation isn't very telling here.
- (void)setImage:(UIImage *)anImage {
assert(anImage);
CGImageRef cgimage = anImage.CGImage;
assert(cgimage);
}
I'm pretty sure, the UIImage is correct since it's fetched from the app bundle. So far I haven't been able to reproduce the case but I do see some user crash reports.
One more possibility that I've come across in the docs:
If the UIImage object was initialized using a CIImage object, the value of the property is NULL.
Another possibility is that you have multiple threads accessing the same UIImage object at the same time; this was happening in my code with the same symptom.
That might explain your sporadic crash reports, too, since the access patterns would depend on timing that changes from run to run.
To the best of my knowledge, if the first assert passes(showing that anImage is not nil) it means that it could not load the image. Check to make sure that the image is being copied into your bundle.
A case where anImage would be non-nil but cgimage would be nil could be contrived as follows:
UIImage* myImage = [[UIImage alloc] initWithCGImage:nil];
As previous answers have indicated there are other ways one could find themselves in such a scenario.

Must I release this image?

I'm creating an UIImage like this:
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
Am I responsible for releasing this?
No.
Return Value
An autoreleased image
object containing the contents of the
current bitmap graphics context.
(doc link)
Read the documentation here. As it's written there, it returns an autoreleased object. You don't have to release it. If you want to keep it, you need to retain it somehow.

How to convert NSData to CGDataProviderRef on iphone sdk?

I am getting EXEC_BAD_ACCESS errors in a CGContextDrawImage call and trying to trace it back.
I have a PNG image and running a UIImagePNGRepresentation o it gives me NSData instance.
I need to convert this to CGImageRef instance, so I can run the CGImageCreateWithPNGDataProvider method with this CGImageRef.
I tried two ways:
1) To cast it.
CGImageRef ref = (CGDataProvider)nsdata;
2) To run CGDataProviderCreateWithCFData(nsdata);
First case returns empty image, though command doesnt fail.
The reason I tried the second case, even though I have NSData and not CFData is because I remember reading it accepts both.
Whatever be the reason, it is failing due to this.
Is there a way I can use my PNG NSData to create a CGImage that is not corrupted?
Please help.
THanks
Your second try is almost right. CFData and NSData are “toll-free bridged”. Anything that accepts a CFDataRef also accepts NSData (and vice-versa) — you just have to cast correctly.
You need:
CGDataProviderCreateWithCFData((CFDataRef)myNSData);
The first is very wrong. You can not turn an NSData instance into a CGImageRef simply by casting it.
The second should work fine. You will have to cast the NSData instance to a CFDataRef but that is perfectly legal due to what Apple calls toll-free bridging.
Here is another and much easier method:
NSData* data = ... get raw image data from somewhere (PNG, JPEG, etc.) ...;
UIImage* image = [UIImage imageWithData: data];
CGImageRef imageRef = image.CGImage;
I prefer to use the higher level UIImage methods to load images and then use image.CGImage for lower level Core Graphics functions. Don't forget to properly retain the UIImage if you need to keep it around.

How to store an image in Core Data?

Just a guess: I make an attribute and make it's type "binary". But in the end, how would I use that? I guess there is an NSData behind the scenes? So that attribute actually takes an NSData?
This question has been asked a number of times and the answer is a bit more complex.
When it comes to binary data you should determine how to store it based on the expected size of data you are going to be working with. The rule is:
Less than 100K;store as a binary property in your main table
Less than 1M; store as a binary property in a ancillary table to avoid over fetching
Greater than 1M; store on disk and store its file path in the Core Data table.
In addition, when you are storing images, it is recommended to store them in a standard format such as JPG or PNG. By using the transformable property type you can actually have your subclass give the appearance of accessing the UIImage class when the actual store is a PNG representation. I go into this in detail in the bog post on Cocoa Is My Girlfriend.
Update
The reason behind storing > 1M binary data on disk is because of the cache. The NSPersistentStoreCoordinator will keep a cache of data so that when your app asks for the "next" object it doesn't need to go back out to disk. This cache works really well. However it is small, very small on iOS. If you pull in a big piece of binary data you can easily blow out that entire cache and your entire app suffers greatly.
That's correct, use binary which is represented as a NSdata object, then u can use uiimages imageWithData class method in order to retrieve your images.
Marcus's answer works, but it's not the easy way - and, AFAICT, it's not how Apple "intends" you to do it. In theory, you should be able to just mark the image as "Transformable", and CD would do everything automatically.
That doesn't work because UIImage is "missing" the implementation of NSCoding - Apple's serialization system - and so the default transformer from CD has no idea how to save an image.
The alternative, which requires a little code, but is still simpler than Marcus's approach ... allegedly you run into Apple's bugs. Apple's CoreData implementation (allegedly) has been broken since 2008 - the custom transformers are ignored for any data-store of type "Binary".
Going back to "CD would do everything automatically, if only UIImage were setup right" ... IMHO, UIImage ought to be serializable, even if the "Default" serialization isn't ideal for all cases. If you fix that, then lo and behold ... CoreData starts saving/laoding UIImage instances with zero coding from you.
Here's code to make your UIImage instances all serializable. All you have to do is import this category / copy paste this category into the classes where you want the "upgraded" UIImage version.
#implementation UIImage (MyCategory)
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeDataObject:UIImagePNGRepresentation(self)];
}
- (id)initWithCoder:(NSCoder *)decoder
{
NSData *data = [[decoder decodeDataObject] retain];
return [[UIImage alloc] initWithData:data];
}
#end

NSKeyedArchiver/NSuserDefaults - saving a CGRect

My app involves a user being able to drag specific items such as volume sliders and UIImage views. My goal is to be able to save the locations of the items so that the user can not have to drag the items again after re-launch. Unfortunately, I have seen errors along taking the 'saving CGRect rect = item.frame to NSuserDefaultsorNSKeyedArchiver' route. Any other suggestions?
You could use NSStringFromgCGRect() and CGRectFromString() to store them.
If you're using keyed archiving on iPhone, UIKit specifically supports an addition that looks like this: -encodeCGRect:forKey: and does precisely what you want. See the Apple docs for NSCoder's UIKit additions.
If you're using NSUserDefaults, you don't get the luxury of using an explicit encoder for CGRects, but as some other answers say, you could save them as strings using NSStringFromCGRect() and its reverse.
You would probably encode CGRect using NSData and then store NSData object using NSUserDefaults or NSKeyedArchiver.
I'm not sure what errors exactly are you referring in your question, maybe more explanation needed?
You have to convert the floats in the CGRect to NSNumbers and then put them in a Dictionary.
If you need to store a lot of CGRects, I recommend creating a little lightweight class with the CGRect elements as archivable NSNumber attributes. Create an initializer that takes a CGRect. Then write quick NSCoder protocol methods so the entire object can be archived
That way you can you quickly and easy convert, store and then retrieve the CGRects.