Почему UIBezierPath быстрее, чем путь Core Graphics? - PullRequest
86 голосов
/ 13 июня 2011

Я играл с путями рисования и заметил, что, по крайней мере, в некоторых случаях UIBezierPath превосходит то, что, как я думал, будет эквивалентом Core Graphics.Приведенный ниже метод -drawRect: создает два пути: один UIBezierPath и один CGPath.Пути идентичны, за исключением их расположения, но для обхода CGPath требуется примерно вдвое больше времени, чем для UIBezierPath.

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

    // Create the two paths, cgpath and uipath.
    CGMutablePathRef cgpath = CGPathCreateMutable();
    CGPathMoveToPoint(cgpath, NULL, 0, 100);

    UIBezierPath *uipath = [[UIBezierPath alloc] init];
    [uipath moveToPoint:CGPointMake(0, 200)];

    // Add 200 curve segments to each path.
    int iterations = 200;
    CGFloat cgBaseline = 100;
    CGFloat uiBaseline = 200;
    CGFloat xincrement = self.bounds.size.width / iterations;
    for (CGFloat x1 = 0, x2 = xincrement;
         x2 < self.bounds.size.width;
         x1 = x2, x2 += xincrement)
    {
        CGPathAddCurveToPoint(cgpath, NULL, x1, cgBaseline-50, x2, cgBaseline+50, x2, cgBaseline);
        [uipath addCurveToPoint:CGPointMake(x2, uiBaseline)
                  controlPoint1:CGPointMake(x1, uiBaseline-50)
                  controlPoint2:CGPointMake(x2, uiBaseline+50)];
    }
    [[UIColor blackColor] setStroke];
    CGContextAddPath(ctx, cgpath);

    // Stroke each path.
    [self strokeContext:ctx];
    [self strokeUIBezierPath:uipath];

    [uipath release];
    CGPathRelease(cgpath);
}

- (void)strokeContext:(CGContextRef)context
{
    CGContextStrokePath(context);
}

- (void)strokeUIBezierPath:(UIBezierPath*)path
{
    [path stroke];
}

Оба пути используют CGContextStrokePath (), поэтому я создал отдельные методы для обхода каждого пути, чтобыЯ вижу время, используемое каждым путем в Инструментах.Ниже приведены типичные результаты (перевернутое дерево вызовов);Вы можете видеть, что -strokeContext: занимает 9,5 с., в то время как -strokeUIBezierPath: занимает всего 5 с.:

Running (Self)      Symbol Name
14638.0ms   88.2%               CGContextStrokePath
9587.0ms   57.8%                 -[QuartzTestView strokeContext:]
5051.0ms   30.4%                 -[UIBezierPath stroke]
5051.0ms   30.4%                  -[QuartzTestView strokeUIBezierPath:]

Похоже, что UIBezierPath каким-то образом оптимизирует путь, который он создает, или я создаюCGPath наивным способом.Что я могу сделать, чтобы ускорить мой рисунок CGPath?

1 Ответ

149 голосов
/ 15 июня 2011

Вы правы в том, что UIBezierPath является просто оболочкой target-c для Core Graphics и, следовательно, будет работать сравнимо.Разница (и причина вашей дельты производительности) в том, что ваше CGContext состояние при рисовании вашего CGPath напрямую отличается от этой настройки на UIBezierPath.Если вы посмотрите на UIBezierPath, у него есть настройки для:

  • lineWidth,
  • lineJoinStyle,
  • lineCapStyle,
  • miterLimit и
  • flatness

При проверке вызова (разборки) на [path stroke] вы заметите, что он конфигурирует текущий графический контекст на основе этих предыдущихзначения перед выполнением вызова CGContextStrokePath.Если вы сделаете то же самое до рисования CGPath, он будет выполнять то же самое:

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

    // Create the two paths, cgpath and uipath.
    CGMutablePathRef cgpath = CGPathCreateMutable();
    CGPathMoveToPoint(cgpath, NULL, 0, 100);

    UIBezierPath *uipath = [[UIBezierPath alloc] init];
    [uipath moveToPoint:CGPointMake(0, 200)];

    // Add 200 curve segments to each path.
    int iterations = 80000;
    CGFloat cgBaseline = 100;
    CGFloat uiBaseline = 200;
    CGFloat xincrement = self.bounds.size.width / iterations;
    for (CGFloat x1 = 0, x2 = xincrement;
         x2 < self.bounds.size.width;
         x1 = x2, x2 += xincrement)
    {
        CGPathAddCurveToPoint(cgpath, NULL, x1, cgBaseline-50, x2, cgBaseline+50, x2, cgBaseline);
        [uipath addCurveToPoint:CGPointMake(x2, uiBaseline)
                  controlPoint1:CGPointMake(x1, uiBaseline-50)
                  controlPoint2:CGPointMake(x2, uiBaseline+50)];
    }
    [[UIColor blackColor] setStroke];
    CGContextAddPath(ctx, cgpath);

    // Stroke each path
    CGContextSaveGState(ctx); {
        // configure context the same as uipath
        CGContextSetLineWidth(ctx, uipath.lineWidth);
        CGContextSetLineJoin(ctx, uipath.lineJoinStyle);
        CGContextSetLineCap(ctx, uipath.lineCapStyle);
        CGContextSetMiterLimit(ctx, uipath.miterLimit);
        CGContextSetFlatness(ctx, uipath.flatness);
        [self strokeContext:ctx];
        CGContextRestoreGState(ctx);
    }
    [self strokeUIBezierPath:uipath];

    [uipath release];
    CGPathRelease(cgpath);
}

- (void)strokeContext:(CGContextRef)context
{
    CGContextStrokePath(context);
}

- (void)strokeUIBezierPath:(UIBezierPath*)path
{
    [path stroke];
}

Снимок с инструментов: Instruments snapshot showing equal performance

...