views:

145

answers:

1

Hi,

in a simple drawing application I have a model which has a NSMutableArray curvedPaths holding all the lines the user has drawn. A line itself is also a NSMutableArray, containing the point objects. As I draw curved NSBezier paths, my point array has the following structure: linePoint, controlPoint, controlPoint, linePoint, controlPoint, controlPoint, etc... I thought having one array holding all the points plus control points would be more efficient than dealing with 2 or 3 different arrays.

Obviously my view draws the paths it gets from the model, which leads to the actual question: Is there a way to optimize the following code (inside the view's drawRect method) in terms of speed?

int lineCount = [[model curvedPaths] count];

// Go through paths
for (int i=0; i < lineCount; i++)
{
    // Get the Color
    NSColor *theColor = [model getColorOfPath:[[model curvedPaths] objectAtIndex:i]];

    // Get the points
    NSArray *thePoints = [model getPointsOfPath:[[model curvedPaths] objectAtIndex:i]];

    // Create a new path for performance reasons
    NSBezierPath *path = [[NSBezierPath alloc] init];

    // Set the color
    [theColor set];

    // Move to first point without drawing
    [path moveToPoint:[[thePoints objectAtIndex:0] myNSPoint]];

    int pointCount = [thePoints count] - 3;

    // Go through points
    for (int j=0; j < pointCount; j+=3)
    {
        [path curveToPoint:[[thePoints objectAtIndex:j+3] myNSPoint] 
             controlPoint1:[[thePoints objectAtIndex:j+1] myNSPoint]
             controlPoint2:[[thePoints objectAtIndex:j+2] myNSPoint]];
    }

    // Draw the path
    [path stroke];

    // Bye stuff
    [path release];
    [theColor release];
}

Thanks, xonic

+2  A: 

Hey xon1c, the code looks good. In general it is impossible to optimize without measuring performance in specific cases.

For example, lets say the code above is only ever called once. It draws a picture in a view and it never needs redrawing. Say the code above takes 50 milliseconds to run. You could rewrite it in openGL and do every optimisation under the sun and get that time down to 20 milliseconds and realistically the 30 milliseconds that you have saved makes no difference to anyone and you pretty much just wasted your time and added a load of code-bloat that is going to be more difficult to maintain.

However, if the code above is called 50 times a second and most of those times it is drawing the same thing then you could meaningfully optimise it.

When it comes to drawing the best way to optimise is to is to eliminate unnecessary drawing.

Each time you draw you recreate the NSBezierPaths - are they always different? You may want to maintain the list of NSBezier paths to draw, keep that in sync with your model, and keep drawrect solely for drawing the paths.

Are you drawing to areas of your view which don't need redrawing? The argument to drawrect is the area of the view that needs redrawing - you could test against that (or getRectsBeingDrawn:count:), but it may be in your case that you know that the entire view needs to be redrawn.

If the paths themselves don't change often, but the view needs redrawing often - eg when the shapes of the paths aren't changing but their positions are animating and they overlap in different ways, you could draw the paths to images (textures) and then inside drawrect you would draw the texture to the view instead of drawing the path to the view. This can be faster because the texture is only created once and uploaded to video memory which is faster to draw to the screen. You should look at Core Animation if this is what you need todo.

If you find that drawing the paths is too slow you could look at CGPath

So, on the whole, it really does depend on what you are doing. The best advice is, as ever, not to get sucked into premature optimisation. If your app isn't actually too slow for your users, your code is just fine.

mustISignUp
Great point there! Saving the paths didn't come to my mind but is actually a very good idea. Thing is, I have a transparent view over the whole Desktop in order to let the user draw on various windows, or write notes, whatever. So the paths do not change very often, but get drawn often and a user could erase one or more. I think the texture won't quite do the job. But just now I've discovered the containsPoint: method of NSBezierPath which will even optimise my 'erase-path-code'! Thx alot dude!
xon1c