How to access CGContext for creating CGLayer offscreen - iphone

Intention: creating CGLayer offscreen, draw some complicated stuff on it from second thread and use it later for quick drawing on main thread
Problem: CGLayerCreateWithContext(context, size, info) expects an already existing CGContext so that it knows for what kind of context it needs to be optimized.
Solution I found so far: CGContextRef ctx = UIGraphicSetCurrentContext()
but this function doesn't seem to exist anymore.
Question: Isn't there another way to access something like a default context? Or do i really need to wait for the first drawRect: call just for accessing UIGraphicsGetCurrentContext() and creating all CGLayers from main thread with one wasted drawing run?

you can create an image context by doing something like:
UIGraphicsBeginImageContext(rect);
// your drawing code
UIGraphicsEndImageContext();
with that said, i'm not sure you can do this from a thread other than main. worth a try, though.

Related

issues with CGContextRef

I am working with drawing project, as we know to draw something we need to get call this function, UIGraphicsGetCurrentcontext, So I am also calling it wherever, I need it, either its clear, or erase . What I am doing is in every function, I am calling it this way
CGContextRef context = UIGraphicsGetCurentContext()
What I want to know, is whether I get the same context whenever I call this function? whether the pointer points to same context?
Because my eraser works in some cases and fails in some cases.
Below is my eraser function
- (void)erase
{
[m_curImage drawAtPoint:CGPointMake(0, 0)];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
}
Regards
Ranjit
You can only use UIGraphicsGetCurentContext() inside of the drawRect method, outside of that it will not be configured with a valid context.
You have to create a custom view and write your erase() method code in the drawRect method.

Using GCD/asynchronous code in drawRect:

I'm trying to load an image from a url inside of drawRect:. Our image-loading code is a method that looks like this: - (void) loadImage:(NSURL*)url done:(void(^)(UIImage*))done;, which creates an asynchronous NSURLConnection and calls back with the image.
So, my code in drawRect: looks like this:
CGContextRef context = UIGraphicsGetCurrentContext()
[service loadImage:url done:^(UIImage * image){
CGContextDrawImage(context, frame, image.CGImage);
}];
Unfortunately, this doesn't work. The image is never drawn.
I've also tried using synchronous connections ([NSData dataWithContentsOfUrl:]), but it blocks the thread and slows things down unnecessarily. I don't want to use a UIImageView.
What is the correct way to do this? Thanks!
I think the correct way would either be to load the image beforehand or draw the image as soon as you receive it by either calling "setNeedsDisplayInRect:" or doing your own drawing handling in a separate method (iOS might discourage you from doing this though, I am not sure). Obviously the setNeedsDisplayInRect variation would require you to have the image at hand for drawing the second time around (cache it).
If you load the image asynchronously your drawRect method will moste likely have exited before the image is loaded and the draw focus is no longer locked on your drawRect, thus making it impossible to draw the image in the context. Did you try to set a breakpoint to verify this though? I might be wrong about the locking, but thought I read something alike this at some point.
Think it through--why would it run? There's no guarantee that context is still the current context (or even that its backing store is still fully valid) by the time the block is entered. By using an asynchronous callback, be it a block or a delegate method or something else, you're intentionally deferring code to a later point in time. The call to -drawRect: has ended and is off the stack long before your block is entered.
The first thing I would try is to make sure the async handler runs on the main thread:
[service loadImage:url done:^(UIImage * image) {
dispatch_async(dispatch_get_main_queue(), ^{
CGContextDrawImage(context, frame, image.CGImage);
});
}];
If that does not help, I would start to wonder if the context is still valid. The pointer is, but the context probably not. Maybe you could move the context acquisition to the handler:
[service loadImage:url done:^(UIImage * image) {
dispatch_async(dispatch_get_main_queue(), ^{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextDrawImage(context, frame, image.CGImage);
…
});
}];
Not sure if that makes sense API-wise, I don’t do much stuff like this.
Have you tried:
__block CGContextRef context = UIGraphicsGetCurrentContext()
[service loadImage:url done:^(UIImage * image){
CGContextDrawImage(context, frame, image.CGImage);
}];
You may have to do the same thing with frame, so when the block returns it knows what context and frame it should modify.

Unable to release a CGContextRef (context is from a CGLayer)

When you create a CGLayer like so, and then get the context...it appears to be impossible to release the CGContextRef?
Releasing the CGLayerRef itself (apparently) works fine.
You'd think you could release the CGContextRef just before releasing the CGLayer - but no? Nor can you release the CGContextRef just after releasing the CGLayer.
If you release the CGContextRef, the app crashes.
CGLayerRef aether = CGLayerCreateWithContext(
UIGraphicsGetCurrentContext(), CGSizeMake(1024,768), NULL);
CGContextRef weird = CGLayerGetContext(aether);
// paths, strokes, filling etc
// paths, strokes, filling etc
// try releasing weird here
CGLayerRelease(aether);
// or, try releasing weird here
Does anyone know what is going on here? (Note further that CGContextRelease is indeed just the same as CFRelease, with some nil checking.)
In fact should you never manually release CGContextRef? Does anyone know? Cheers.
CGContextRelease(weird); // impossible, not necessary, doesn't work???
Regarding Joel's spectacular answer below:
Is releasing the CGLayerRef correct and proper? Joel has pointed out:
"Yes, since the function you're obtaining it from has 'Create' in its signature. See: documentation/CoreFoundation/"
You do not own the context returned from CGLayerGetContext, and so should not release it*. In particular, see http://developer.apple.com/library/mac/#documentation/CoreFoundation/Conceptual/CFMemoryMgmt/Concepts/Ownership.html#//apple_ref/doc/writerid/cfGetRule for information regarding 'Get' functions in Core Foundation.
*: at least, you shouldn't release it given your example code. If you retained it first (CGContextRetain(weird)), then you should have a CGContextRelease to balance it.

Quartz PDF-context based function fails (occasionally) with BAD_ACCESS

Im Getting a Frequent enough BAD_ACCESS when i call this Quartz function:
CGContextDrawPDFPage ((CGContextRef)context, (CGPDFPageRef)pageRef);
Here is how i call it:
CGContextRef context = UIGraphicsGetCurrentContext();
//translate, scale
CGPDFPageRef myPageRef = CGPDFDocumentGetPage ([PDFDocument sharedPDFDocument].documentData, pageNumber);
CGContextDrawPDFPage (context, myPageRef);//BAD_ACCESS HERE
return UIGraphicsGetImageFromCurrentImageContext();//autoreleased- saved on return
//close context
Neither variable is Nil, or have been released/auto released.
Here is the Stack trace, from the debugger:
Can anyone shed any light on this? Even some pointers on how i might investigate this better. it might not even be function specific.
I probobly should mention this function is performed on a separate thread.
Check the documentation for that returns your myPageRef and see if you have to retain it.
At the time of writing the question, UIGraphicsGetImageFromCurrentImageContext and such functions where not thread safe, this was my issue. As of iOs 4.1+ many UI function are now thread safe.

_NSAutoreleaseNoPool Breaking but No Helpful Stack Trace

I am getting the message:
*** _NSAutoreleaseNoPool(): Object 0x3f43660 of class UICFFont
autoreleased with no pool in place -
just leaking
I have placed a break point using the symbol _NSAutoreleaseNoPool and the program does break, however, the stack trace does not show me any of my code only some UIView and Core Animation layer code.
alt text http://img.skitch.com/20100614-fw7u4qtb5bprpwrkh9rdkwn3rq.png
Is there a better way to get to the bottom of the issue? There is apparently a thread that does not have an auto release pool, but I can't figure out where.
Thanks.
Are you using CATiledLayer instances ? This is the only type of layer I know that can have their drawLayer:inContext: method called from an arbitrary thread:
As more data is required by the
renderer, the layer's
drawLayer:inContext: method is called
on one or more background threads to
supply the drawing operations to fill
in one tile of data. The clip bounds
and CTM of the drawing context can be
used to determine the bounds and
resolution of the tile being
requested.