Почему UIBezierPath быстрее, чем путь Core Graphics?

Я играл с путями рисования и заметил, что, по крайней мере, в некоторых случаях 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 ответ

Решение

Вы правы в этом UIBezierPath это просто оболочка Objective-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];
}

Снимок с инструментов:Снимок инструментов, показывающий равную производительность

Другие вопросы по тегам