views:

82

answers:

0

Hi,

I'm working on a drawing app. Currently all the user's drawings are drawn into separate CALayers. To improve the responsiveness of freehand drawing when the CALayer's path gets large, I am caching parts of the path at regular intervals in a CGLayer. The following code shows how I have implemented this within the CALayer's drawInContext: method.

- (void)drawInContext:(CGContextRef)context {

     if (self.shape == kDrawFreehand & [self.freehandPoints count] != 4) {
          CGContextDrawLayerAtPoint(context, CGPointZero, cachedLayer);
     }

     if ([self.freehandPoints count] == 4) {
          CGLayerRef newSublayer = CGLayerCreateWithContext(context, self.bounds.size, NULL);     // Create temporary CGLayer
          CGContextRef sublayerContext = CGLayerGetContext(newSublayer);                          // Get the layer's context

          CGContextDrawLayerAtPoint(sublayerContext, CGPointZero, cachedLayer);                   // Draw all previous drawings into the temp layer

          [self setFormattingForContext:sublayerContext];                                         // Helper method to set line cap, line width etc

          CGPathRef layerPath = CGPathCreateCopy([self drawSmoothLine]);                          // Creates the new part of the line to append to the layer                         

          CGContextBeginPath(sublayerContext);                                                    // \ 
          CGContextAddPath(sublayerContext, layerPath);                                           //  } Draw the new path
          CGContextStrokePath(sublayerContext);                                                   // /

          cachedLayer = newSublayer;                                                              // Reassign cached layer so as it now contains all drawings                                                      

          CGContextDrawLayerAtPoint(context, CGPointZero, cachedLayer);                           // Draw the layer

          [self.freehandPoints removeObjectsInRange:NSMakeRange(0, [freehandPoints count] - 1)];
     } else {
          // Draw as normal directly into the current graphics context...
     }
}

This works fine, except for a couple of things:

1) There is a memory leak associated with the constant creation of newSublayer objects (or at least I believe there is - I am new to Instruments, but this is what it seemed to suggest). When I try to release newSublayer after assigning it to cachedLayer, I get an EXC_BAD_ACCESS error. How can I correctly and safely assign cachedLayer AND release newSublayer?

2) When drawing with <1 alpha lines, the joins where one line ends and the next starts overlap and are visible. This is a continuous freehand drawing and so this isn't ideal. It isn't 100% necessary that I give the user the option to draw with <1 alpha, so I may choose to ignore this in favour of the performance increase if necessary (unless there is an easy fix).

So the questions I'm asking are:

  • Is this a sensible way to approach my performance problem? I tried using multiple CGLayers and iterating over them each call to drawInContext: but this took too long.

  • Is there a better way to append drawings to an existing CGLayer?

  • Would using a CAShapeLayer instead of a CALayer give me a significant performance increase? Are there many limitations to CAShapeLayers as far as simple drawing such as this?

  • Also, I have seen it mentioned that NSTimers can be used to throttle drawRect refresh times, and that this might help responsiveness to user drawing inputs. Can somebody perhaps point me in the direction of some documentation or tutorials that demonstrates how to go about doing this please?

Regards,

Stuart