How to convert NSData to CGDataProviderRef on iphone sdk? - iphone

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.

Related

Using CGDataProviderCreateWithData callback

I'm using CGDataProviderCreateWithData to (eventually) create a UIImage from a malloced array of bytes. I call CGDataProviderCreateWithData like this:
provider = CGDataProviderCreateWithData(NULL, dataPtr, dataLen, callbackFunc);
where
dataPtr is the previously malloced array of data bytes for the image,
dataLen is the number of bytes in the dataPtr array, and
callbackFunc is as described in the CGDataProviderCreateWithData documentation:
void callbackFunc(void *info, const void *data, size_t size);
The callback function is called when the data provider is released so I could free() dataPtr there, but I may want to continue using it (dataPtr) and at some later stage free it. This block of code will be called multiple times, and the flow will look something like:
malloc(dataPtr)
create image (call CGDataProviderCreateWithData etc)
display image
release image (and so release data provider created by CGDataProviderCreateWithData)
continue to use dataPtr
free(dataPtr)
so 1..6 may be executed multiple times. I don't want dataPtr hanging around for the entire execution of the program (and it may change in size anyway), so I want to malloc/free it as necessary.
The problem is that I can't free(dataPtr) in the callback from CGDataProviderCreateWithData because I still want to use it, so I want to free it some time later - and I can't free it until I know that the data provider no longer needs it (as far as I can tell CGDataProviderCreateWithData uses the array I pass, it doesn't take a copy).
I can't do (1) above until I know it is ok to free and re-malloc dataPtr, so what I really want to do is block waiting for the data provider to be freed (well, I want to know whether I should re-enter the 1..6 block of code, which I can't do until the data provider is freed). It will be - I create the data provider, create the image and immediately display it and release the data provider. The trouble is that the data provider isn't actually released until the UIImage is released and is finished with it.
I'm reasonably new to objective-c and iOS. Am I missing something obvious?
If you malloc the memory for the provider's data, you really want to to free it in the callback. Do not be tempted to try to circumvent this in any manner. It be too easy to leak and would be susceptible to simple memory issues.
Having said that, there are two simple solutions that address your question. Either:
Make your own copy of the data that you'll manage separately from the provider; or
Instead of using the void * renditions of the CGDataProvider methods, use the CFData rendition (e.g. CGDataProviderCreateWithCFData) and then you can maintain your own strong reference to this data object (or if in non-ARC code, do your own retain of the data object). The object will not be deallocated until all strong references are resolved (or, in non-ARC code, all of your manual retain calls are resolved with a corresponding release or autorelease call).
With either of these approaches, you can continue to let CGProviderRef manage the memory as it sees fit, but you can continue to use the data object for your own purposes, too.
I ran into a similar problem as well. I wanted to use CGImageCreateWithJPEGDataProvider, so used CGDataProviderRef with a malloc'd byte array. I was getting the same problem, when trying to free the byte array at some point after creating the Data Provider Reference.
It occurs because the data provider reference takes over the ownership of the byte array, so can only be freed by the reference, when it is done with it.
I agree with #TheBlack in that if you need the data elsewhere, make a copy of it.
What I'm doing is essentially the same to what you want to achieve, I just took different route.
This whole process is wrapped into NSOperation so you have control over scheduling memory usage.
CGImageRef imageRef = [image CGImage];
NSUInteger width = CGImageGetWidth(imageRef);
NSUInteger height = CGImageGetHeight(imageRef);
CGColorSpaceRef colorSpace = CGImageGetColorSpace(imageRef);
NSUInteger bitsPerComponent = CGImageGetBitsPerComponent(imageRef);
UInt8 *rawData = calloc((height * width * bitsPerComponent), sizeof(UInt8));
NSUInteger bytesPerRow = CGImageGetBytesPerRow(imageRef);
CGContextRef context = CGBitmapContextCreate(rawData, width, height,
bitsPerComponent, bytesPerRow, colorSpace,
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);
if (context)
{
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
// This is the place you get access to data trough rawData. Do whatever you want with it, when done, get image with
CGImageRef newImg = CGBitmapContextCreateImage(context);
}
OR
subclass CALayer, override drawInContext and use CGBitmapContextGetData to get raw data. Don't have much experience with this though, sorry, but if you want to see instant changes
on image based on user input, I'd do it this way:
Subclass UIView and CALayer which becomes layer for view and displays image. View gets input and image data (from CGBitmapContextGetData) in layer class is manipulated based on input.
Even CATiledLayer can be used for huge images in this way. When done with image, just release UIView and replace it with new one. I'll gladly provide help if you need any, just ping here.

-[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.

Separate NSData into smaller NSMutableData objects

I am unsure of how to separate a NSData object into smaller parts so that I can send it over bluetooth. I believe it is a method similar to this:
- (void)getBytes:(void *)buffer range:(NSRange)range
I do not know what to pass in for the buffer. Do I just pass in a NSMutableData object to hold the bytes that I pull out of the original NSData?
Thanks
You can use the -subdataWithRange: method.

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

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.