Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a CGLayer from a UIView for off-screen drawing

I have read what I believe to be the relevant parts of the Quartz 2D Programming Guide, but cannot find an answer to the following (they don't seem to talk a lot about iOS in the document):

My application displays a drawing in a UIView. Every now and then I have to update the drawing in some way, e.g. change the fill colour of one of the shapes (I keep CGPathRefs to the important shapes to be able to redraw them with a different fill colour later). As described in the Section "Drawing With a CGLayer" on page 169 of the aforementioned document, I was thinking of drawing the entire drawing into a CGContext that I would obtain from a CGLayer, like so:

CGContextRef offscreenContext = CGLayerGetContext(offscreenLayer);

Then I could do my updating off-screen into the CGContext and draw the CGLayer into my UIView in the UIView's drawRect: method, like so:

CGContextDrawLayerAtPoint(viewContext, CGPointZero, offscreenLayer);

The problem I am having is, where do I get my CGLayer from? My understanding is I have to make it using CGLayerCreateWithContext and supply a CGContext as a parameter from which it inherits most of it's properties. Obviously, the right context would be the context of the UIView, that I am getting with

CGContextRef viewContext = UIGraphicsGetCurrentContext();

but if I am not mistaken, I can only get that within the drawRect: method and it is not valid to assume that the context I am given there will be the same one next time the method is called, i.e. I can only use that CGContext locally within the method.

So, how can I get a CGContext that I can use to initialise my CGLayer to create an offscreen CGContext to draw into and then draw the entire layer back into my UIView's CGContext?

PS: While you're at it; if anything above does not make sense or is not sane, please let me know. I am just starting to get my head around Quartz 2D.

like image 911
McKrassy Avatar asked Nov 14 '22 03:11

McKrassy


1 Answers

First of all, if you are doing it from in an iOS environment, I think you are right. The documentation clearly said that the only way to obtain a CGContextRef is by

CGContextRef ctx = UIGraphicGetCurrentContext();

Then you use that context for creating the CGLayer with

CGLayerRef layer = CGLayerCreateWithContext(ctx, (CGSize){0,0}, NULL);

And if you want to draw on that layer, you have to draw it with the context you get from the layer. (It is somewhat different from the context you passed in earlier to create the CGLayer). Im guessing the CGLayerCreateWithContext saves the information it can get from the context passed in, but not everything. (One of the example is the ColorSpace information, you have to re-specify when you fill something with the context from CGLayer).

You can get the CGLayer context reference from the CGLayerGetContext() function and use that to draw.

CGContextRef layerCtx = CGLayerGetContext(layer);
CGContextBeginPath(layerCtx);
CGContextMoveToPoint(layerCtx, -10, 10);
CGContextAddLineToPoint(layerCtx, 100, 10);
CGContextAddLineToPoint(layerCtx, 100, 100);
CGContextClosePath(layerCtx);

One point that I found out is when you draw something offscreen, it automatically clips the thing offscreen. (make sense, so it doesnt draw things that is not seen) but when you move the layer (using the matrix transformation). The clipped path is not showing (missing).

One last thing, if you save the reference to a layer into a variable and later on you want to draw it, you can use CGContextDrawLayerAtPoint() method like

CGContextDrawLayerAtPoint(ctx, (CGPoint) {newPointX, newPointY}, layer);

It will sort of "stampt" or "draw" the layer at that newPointX and new PointY coordinate.

I hope that answer your question, if its not please let me know.

like image 133
Rpranata Avatar answered Dec 28 '22 06:12

Rpranata