How do i set a CGContextRef property rather than using UIGraphicsGetCurrentContext() - objective-c

Ive been trying to work with UIGraphicsGetCurrentContext and CGContextRef. I was told that using UIGraphicsGetCurrentContext() numerous times is bad and to rather work on using CGContextRef and referring to it.
I have been trying to work on the second part and I am having issues setting the #property and referring to it. Can someone give me a declaration and usage example of this? Tried googling and couldn't find any references to it.
Ta

You probably shouldn't store the return value from UIGraphicsGetCurrentContext in a property. You usually either don't know how long the context will be valid, or the context has a short lifetime anyway. For example, if you're calling UIGraphicsGetCurrentContext from your drawRect: method, you don't know how long that context will survive after you return from drawRect:. If you're calling UIGraphicsGetCurrentContext after calling UIGraphicsBeginImageContextWithOptions, you will probably be calling UIGraphicsEndImageContext soon anyway. It would be inappropriate to keep references to those contexts around.
If you are calling many Core Graphics functions on the same context, then you want to store the context in a local variable. For example, here is a drawRect: method from one of my test projects:
- (void)drawRect:(CGRect)dirtyRect {
NSLog(#"drawRect:%#", NSStringFromCGRect(dirtyRect));
[self layoutColumnsIfNeeded];
CGContextRef gc = UIGraphicsGetCurrentContext();
CGContextSaveGState(gc); {
// Flip the Y-axis of the context because that's what CoreText assumes.
CGContextTranslateCTM(gc, 0, self.bounds.size.height);
CGContextScaleCTM(gc, 1, -1);
for (NSUInteger i = 0, l = CFArrayGetCount(_columnFrames); i < l; ++i) {
CTFrameRef frame = CFArrayGetValueAtIndex(_columnFrames, i);
CGPathRef path = CTFrameGetPath(frame);
CGRect frameRect = CGPathGetBoundingBox(path);
if (!CGRectIntersectsRect(frameRect, dirtyRect))
continue;
CTFrameDraw(frame, gc);
}
} CGContextRestoreGState(gc);
}
You can see that I'm doing a bunch of stuff with the context: I'm saving and restoring the graphics state, I'm changing the CTM, and I'm drawing some Core Text frames into it. Instead of calling UIGraphicsGetCurrentContext many times, I call it just once and save the result in a local variable named gc.

Related

SKPhysicsBody bodyWithPolygonFromPath memory leaks

I have a strange memory leaks when creating Sprite Kit physics bodies with custom shapes. This is how my implementation looks:
CGFloat offsetX = self.frame.size.width * self.anchorPoint.x;
CGFloat offsetY = self.frame.size.height * self.anchorPoint.y;
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 4 - offsetX, 3 - offsetY);
CGPathAddLineToPoint(path, NULL, 66 - offsetX, 3 - offsetY);
CGPathAddLineToPoint(path, NULL, 35 - offsetX, 57 - offsetY);
CGPathCloseSubpath(path);
self.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:path];
CGPathRelease(path);
Everything is going on inside SKSpriteNode method. Instruments tells me about several memory leaks after creating such bodies:
Leaked object:
Malloc 32 Bytes
Size:
32 Bytes
Responsible Library:
PhysicsKit
Responsible Frame:
std::__1::__split_buffer<PKPoint, std::__1::allocator<PKPoint>&>::__split_buffer(unsigned long, unsigned long, std::__1::allocator<PKPoint>&)
The CGPathRelease(path); line is necessary - without it I'm getting more memory leaks about CGPath which is understandable. When I'm using this implementation instead (for testing purposes):
CGFloat radius = MAX(self.frame.size.width, self.frame.size.height) * 0.5f;
self.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:radius];
...everything is working well, without memory leaks. I wonder if this is Sprite Kit bug or I'm doing something wrong.
This is a bug in sprite kit. You will have to wait for a fix.
Does anything change if you do the following?
CGPathRef pathCopy = CGPathCreateCopy(path);
CGPathRelease(path);
self.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:pathCopy];
CGPathRelease(pathCopy);
The only restriction on the path parameter is:
A convex polygonal path with counterclockwise winding and no self intersections. The points are specified relative to the owning node’s origin.
I do not know the values of offsetX and offsetY so I do not know whether the path is correct or not, but assuming that they are both 0, it seems to me like this path is clockwise and not counterclockwise. I would create the path using constants and no variables, just to make sure that it is correct and if it is still leaking I would say it's a bug in PhysicsKit.
As commented in several places, this looks like a bug in the implementation of SKPhysicsBody, which persists at least until iOS 7.1. The reason for this is:
SKPhysicsBody holds an instance variable, '_path', which holds a copy of the initial CGPathRef passed when calling 'bodyWithEdgeChainFromPath' or similiar constructors. This instance variable never gets released, so all paths will stay in memory.
However, you may implement a workaround for this by
(1) subclassing the SKShapeNode, that should hold the SKPhysicsBody,
(2) after creating and assigning the SKPhysicsBody for this node, retrieve the instance variable referring to the CGPathRef of the SKPhysicsBody,
(3) when the shape node is deallocated, check the retain count of the path. If it's > 0, release it and the memory leaks are gone.
There's little code overhead (beside subclassing all shape nodes, that use physics bodies relying on CGPath's). Just do this:
Add an instance variable to your subclass:
{
CGPathRef myPath;
}
Implement the method to retrieve the value for this CGPath in any subclassed SKShapeNode implementation. You might also consider to add this as a general category on SKNode:
- (CGPathRef) getPhysicsBodyPath
{
CGPathRef path = nil;
if ( self.physicsBody ){
object_getInstanceVariable(self.physicsBody, "_path", (void**) &path);
}
return(path);
}
This code will return the CGPathRef instance used by the node's physics body. Note, however, that this must be done immediately after assigning the physics body to the node. At a later time (i.e. in dealloc(), this might return a null value. So, after creating the body, store this value in the instance variable 'myPath'. To make this code work even after Apple might fix the bug, we'll add an additonal retain, which will make sure we can access this object once our SKNode is deallocated (see below):
/*
* we need to keep a copy of the final path reference, that has been created for the physics body.
* retrieving this value during deallocation won't work any more...
*/
myPath = CFRetain([self getPhysicsBodyPath]);
Finally, overwrite the 'dealloc' method and release the path, after your SKNode is released:
- (void) dealloc
{
self.physicsBody = nil;
[super dealloc];
if ( myPath != nil ) {
/* this will work, because we've retained the path for this instance */
CFIndex rc = CFGetRetainCount (myPath);
/* this is our own release ... */
CGPathRelease(myPath);
/* in case Apple has fixed the release, this is OK, otherwise we'll need
* to release the path twice to avoid memory leaks
*/
if ( rc > 1 ) {
CGPathRelease(myPath);
}
}
}
This will finally release this path and fix the memory leaks. This code works for all iOS version up to 7.1 and it also should work on further versions, once Apple finally fixes this bug and SKPhysicsBoy actually releases the path (as they are supposed to do).
I've also experienced similar memory leak, but it was fixed after I removed one line from my viewDidLoad function
skView.showsPhysics = YES;

Issues with threading, blocks, CGImageRef and scope

I have a program where I set up a completion block that is running on a background thread. Inside the block I set up a CGImageRef and then on the main thread set my layer contents. THe problem is, sometimes the app crashes on the main thread portion.
This is the completion block, in the code below both the fullImage and cfFullImage are declared in my .h
requestCompleteBlock completionBlock = ^(id data)
{
// Seems I need to hold onto the data for use later
fullImage = (NSImage*)data;
NSRect fullSizeRect = NSMakeRect(0, 0, self.frame.size.width, self.frame.size.height);
// Calling setContents with an NSImage is expensive because the image has to
// be pushed to the GPU first. So, pre-emptively push it to the GPU by getting
// a CGImage instead.
cgFullImage = [fullImage CGImageForProposedRect:&fullSizeRect context:nil hints:NULL];
// Rendering needs to happen on the main thread or else crashes will occur
[self performSelectorOnMainThread:#selector(displayFullSize) withObject:nil waitUntilDone:NO];
};
The final line of my completion block is to call displayFullSize. That function is below.
- (void)displayFullSize
{
[self setContents:(__bridge id)(cgFullImage)];
}
Do you see or know of any reason why the setContents might fail?
Thanks
Joe
cgFullImage is not retained. The CGImage Core Foundation object is deallocated and you are using a deallocated object.
Core Foundation object pointer types like CGImageRef are not managed by ARC. You should either annotate the instance variable with __attribute__((NSObject)), or change the type of the instance variable to an Objective-C object pointer type like id.

CADisplayLink and static variable magic in Apple OpenGl ES Sample

I would like an explanation of why XCode's OpenGl ES Sample works, please. It does the following to start the drawFrame method (in the blablaViewController.m - name is dependent on the project's name):
//sets up a CADisplayLink to do a regular (draw & update) call like this
CADisplayLink *aDisplayLink = [[UIScreen mainScreen] displayLinkWithTarget:self
selector:#selector(drawFrame)];
[aDisplayLink setFrameInterval:animationFrameInterval];
[aDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
and inside the drawFrame method it does the following:
//start of method
...
static float transY = 0.0f;
...
//Quite a lot of OpenGl code, I am showing only parts of the OpenGL ES1 version:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0f, (GLfloat)(sinf(transY)/2.0f), 0.0f);
transY += 0.075f;
...
//end of method
I don't know a lot of Objective C yet, but the way this transY variable is reset, then incremented in the same method is very weird. Since the GL_MODELVIEW matrix is reset to identity before being shifted, I don't think it could keep an accumulated value in opengl somewhere.
Is the static keyword the trick here? Does Objective C ignore all future variable declarations once something has been declared static once?
Thanks for the help!
Static variables get initializated at compile time in the binary, so only once, and for that reason you're forbidden to assign dynamic values for their initialization. Here, the variable transY is not set to 0.0 at every method call, but just on startup. That's why subsequent calls of the method can retrieve the old value.

Why I can't draw in a loop? (Using UIView in iPhone)

I can draw many things using this :
NSString *imagePath = [[NSBundle mainBundle] pathForResource:#"dummy2.png" ofType:nil];
UIImage *img = [UIImage imageWithContentsOfFile:imagePath];
image = CGImageRetain(img.CGImage);
CGRect imageRect;
double x = 0;
double y = 0;
for (int k=0; k<someValue; k++) {
x += k;
y += k;
imageRect.origin = CGPointMake(x, y);
imageRect.size = CGSizeMake(25, 25);
CGContextDrawImage(UIGraphicsGetCurrentContext(), imageRect, image);
}
}
CGImageRelease(img.CGImage);
So, it works, so, I put it into a command object's execute method. Then, I want to do similar thing, but this time, my execute method only do this:
NSString *imagePath = [[NSBundle mainBundle] pathForResource:#"dummy2.png" ofType:nil];
UIImage *img = [UIImage imageWithContentsOfFile:imagePath];
image = CGImageRetain(img.CGImage);
CGRect imageRect;
double x = inComingX;
double y = inComingY;
imageRect.origin = CGPointMake(x, y);
imageRect.size = CGSizeMake(25, 25);
CGContextDrawImage(UIGraphicsGetCurrentContext(), imageRect, image);
CGImageRelease(img.CGImage);
This time, this is also a Command, and it is the execute method. But I take the for loop away. I will have another method that pass the inComingX , and inComingY into my Command object.
My Drawing method is simply execute the Cmd that passed in my drawingEngine:
-(void)drawInContext:(CGContextRef)context
{
[self.cmdToBeExecuted execute];
}
I also have the assign method to assign the command,:
-(void)assignCmd:(Command* )cmd{
self.cmdToBeExecuted = cmd;
}
And this is the way I called the drawingEngine
for(int k=0; k<5; k++){
[self.drawingEngine assignCmd:[DrawingCmd setDrawingInformation:(10*k):0:#"dummy.png"]];
[self.drawingEngine setNeedsDisplay];
}
It can draw, but the sad thing is it only draw the last one. Why? and how to fix it? I can draw all the things in my First code, but after I take the loop outside, and use the loop in last code, it just only draw the last one. Plz help
That's because setNeedsDisplay does not actually call drawRect:. It simply schedules the view to be redrawn at the next "convenient" time, which is likely the next time, the application re-enters the run-loop. Since you overwrite the remembered command object on each call to the assignment function, by the time, the drawRect: is actually called, only the last assigned command is available and will be drawn.
A better way to do it would be: remember all commands to be drawn instead just the last one, say in an array, like:
#interface MyCanvas {
...
NSMutableArray* commandList;
...
}
and add commands to that array instead of assigning a single command member:
-(void) addCommand:(Command*) cmd {
[self.commandList addObject: cmd];
}
The commands should then be processed in your draw method
for( Command* cmd in self.commandList ) {
[cmd execute ...];
}
Alternatively, you could define "complex" commands, which consist of more than a single drawing step.
(EDIT to answer the question in the comments): Your original code did work, because it does the work all in one place in a single invocation of the appropriate draw method. Your last code does not draw anything at all while it runs. It simply remembers (via command object) that something has to be done, and notifies the view, that it should redraw itself on the next convenient occasion. It is important to note, that setNeedsDisplay will not cause any repainting to be done directly. It simply marks the view as "dirty", which will be picked up by other code in the Cocoa run-time later.
There is another thing in your code which I find slightly suspicious: your method drawInContext: takes a context argument, which is simply ignored. Neither is it passed on to the execute method of you command object, nor is it installed as some kind of "current" drawing context in an instance variable or somesuch. If you expect the code in drawRect: (or the command's execute method) to actually use that context, you have to pass it on to whoever is supposed to use it.

Problem filling NSMutableArray w/ UIImage objects

I am having a baffling issue while trying to fill an NSMutableArray with UIImages.
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderMask;
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
while(...) // <--- Iterates through data sets
{
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, data, numBytes, NULL);
CGImageRef cImage = CGImageCreate(iw, ih, 8, 32, 4 * iw, colorSpace, bitmapInfo, provider, NULL, NO, renderingIntent);
UIImage *finalImage = [UIImage imageWithCGImage:cImage];
[images addObject:finalImage]; // <--- Declared and instantiated earlier
[delegate sendImage:finalImage];
CGImageRelease(cImage);
CGDataProviderRelease(provider);
}
CGColorSpaceRelease(colorSpace);
[delegate operationCompletedWithImages:images];
That is how I have the code running. So I basically have a function running in the while statement that returns the next set of bitmap data, I then create a UIImage and store it into the mutable array.
I've tested this by writing each file to disk and then accessing them which results in the proper set of images. The problem is when using the array to keep data in memory, accessing any image object in the array I get the same exact image over and over. The image is also always the last image of the set.
I've tried testing the contents by setting the array as a UIImageView animation array and by using an NSTimer to cycle the contents. Either way it is just the same image (last image pulled) over and over.
I have this operation running inside a subclassed NSOperation object so it doesn't block the interface. Another interesting aspect here is that when the images array sent with operationCompletedWithImages was giving me the array of duplicate images I tried using the sendImage message and store the images in a different array inside the delegate object (thinking maybe it was a threading issue). This gave me the same results.
I've been stuck on this for over a week with no progress. I've never seen anything like it and I can't find anyone else who has had a similar issue.I would be happy to provide extra information if someone feels it would assist in solving this issue.
If anyone could provide any assistance I would greatly appreciate it.
Not sure if this is the problem, but you should probably be notifying your delegate of the changes on the main thread, not the thread that the NSOperation is running on.
[delegate performSelectorOnMainThread:#selector(sendImage:) withObject:finalImage waitUntilDone:YES];
And likewise for the last message. If the delegate is modifying anything UIKit-related, it must be invoked on the main thread!
One might assume that the NSMutableArray would handle the memory management of the object being added to it, but perhaps because you're dealing with the C level Quartz helpers/ Core wrappers this may not be the case.
In other image manipulating methods I've experimented with they are usually wrapped in an autorelease pool if they are involved with threads.
Have you tried experimenting with release/autorelease on the finalImage?
Figured out the problem. Turned out to be an issue with the backing data pointer.
Where before I was accessing data everytime thinking it would iterate and overwrite with new contents, and the new contents would be plugged into my CGDataProviderRef. This was not the case, I basically ended up supplying a pointer to the same backing data for the Provider. The solution was to copy the data into an NSData object and use that copy for the DataProvider.
So instead of:
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, data, numBytes, NULL);
It would be:
NSData *pxData = [[NSData alloc] initWithBytes:pFrameRGB->data length:numBytes];
CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)pxData);