cocoatoucher
cocoatoucher

Reputation: 1493

Saving and restoring CGContext

I'm trying to save and restore a CGContext to avoid doing heavy drawing computations for a second time and I'm getting the error <Error>: CGGStackRestore: gstack underflow.

What am I doing wrong? What is the correct way to do this?

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();

    if (initialized) {
        CGContextRestoreGState(context);
        //scale context
        return;
    }

    initialized = YES;

    //heavy drawing computation and drawing

    CGContextSaveGState(context);
}

Upvotes: 12

Views: 20612

Answers (3)

abdimuna
abdimuna

Reputation: 785

.. Based on your code !, It seems that you are Restoring the Context before Saving it. First Thing First:

  1. Create a context
  2. Save its state, aka push
  3. Do some stuff with the context
  4. Restore the context aka Pop
  5. General rule for each Store(push) there must be Restore(pop)
  6. Release the context when you are done with it !, This refers to those context/s which they have CGCreate, CGCopy,

Sample code:

        [super drawRect:rect];
        CGContextRef ctx = UIGraphicsGetCurrentContext();
// save context 
        CGContextSaveGState(ctx);
// do some stuff 
        CGContextSetRGBStrokeColor(ctx, 1.0, 0.5, 0.5, 1.0);
        // drawing vertical lines
        CGContextSetLineWidth(ctx, 1.0);
        for (int i = 0; i < [columns count]; i++) {
            CGFloat f = [((NSNumber*) [columns objectAtIndex:i]) floatValue];
            CGContextMoveToPoint(ctx, f+(i*20.5), 0.5);
            CGContextAddLineToPoint(ctx, f+(i*20.5), self.bounds.size.height);
        }
// restore context 
        CGContextRestoreGState(ctx);
// do some other stuff 
        // drawing hozizontal lines
        CGContextSetLineWidth(ctx, 1.0);
         CGContextSetRGBStrokeColor(ctx, 0.12385, 0.43253, 0.51345, 1.0);
        for (int i = 0; i < [columns count]; i++) {
            CGFloat f = [((NSNumber*) [columns objectAtIndex:i]) floatValue];
            CGContextMoveToPoint(ctx, 0.5, f+(i*20.5));
            CGContextAddLineToPoint(ctx,self.bounds.size.width,f+(i*20.5));
        }

        CGContextStrokePath(ctx);
    }
// No context CGContextRelease , since we never used CGContextCreate 

Upvotes: 0

Brad Larson
Brad Larson

Reputation: 170319

I think you might be misinterpreting what CGContextSaveGState() and CGContextRestoreGState() do. They push the current graphics state onto a stack and pop it off, letting you transform the current drawing space, change line styles, etc., then restore the state to what it was before you set those values. It does not store drawing elements, like paths.

From the documentation on CGContextSaveGState():

Each graphics context maintains a stack of graphics states. Note that not all aspects of the current drawing environment are elements of the graphics state. For example, the current path is not considered part of the graphics state and is therefore not saved when you call the CGContextSaveGState() function.

The graphics state stack should be reset at the start of your drawRect:, which is why you're getting errors when you try to pop a graphics state off the stack. Since you hadn't pushed one on, there was none to pop off. All of this means that you can't store your drawing as graphics state on the stack, then restore it later.

If all you are worried about is caching your drawing, that is done for you by the CALayer that backs your UIView (on the iPhone). If all you are doing is moving your view around, it won't be redrawn. It will only be drawn if you manually tell it to do so. If you do have to update part of the drawing, I recommend splitting the static elements off into their own views or CALayers so that only the part that changes is redrawn.

Upvotes: 20

mahboudz
mahboudz

Reputation: 39376

Don't you want to Save first and then Restore? If you are restoring before a save, then there is no context to restore, and you'd get an underflow.

Here is the way I have used it:

CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextClipToRect(context, CGRectMake(stripe[i][8], stripe[i][9], stripe[i][10], stripe[i][11]));
CGContextDrawLinearGradient(context, gradient, CGPointMake(15, 5), CGPointMake(15, 25), 0);
CGContextRestoreGState(context);

or:

  CGContextRef context = UIGraphicsGetCurrentContext();
  CGContextSaveGState(context);
  CGContextAddRect(context, originalRect);
  CGContextClip(context);

  [self drawInRect:rect];

  CGContextRestoreGState(context);

Maybe you are trying to do something else.

Upvotes: 7

Related Questions