为什么UIBezierPath比Core Graphics路径快?


90

我正在研究绘图路径,并且发现至少在某些情况下,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(),因此我创建了单独的方法来描画每个路径,以便可以在Instruments中查看每个路径所用的时间。以下是典型结果(调用树倒置);您可以看到这-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绘制速度?


2
+1听起来有点反话。
Grady Player

1
我通常发现CoreGraphics在绘制线条,路径等时非常慢。我不知道为什么,但是我大多数时候必须使用OpenGL或使用Cocos2D进行有效绘图。当然,我知道它速度更快,但是我真的不明白为什么CG这么慢,考虑到它应该使用OpenGL本身。
Accatyyc 2011年

4
UIBezierPath是一个包装纸CGPathRef。如果您都运行一千万次,然后取平均值,那该怎么办,但是NSDate在操作前后不要使用Instruments而是两个对象。

1
@WTP,结果在设备上和在模拟器中都是一致的,并且无论-drawRect:被调用几十次还是几百次都不会改变。我已经在模拟器上尝试了多达80000个曲线段(对于该设备来说太多了)。结果始终是相同的:CGPath花费的时间是UIBezierPath的两倍,即使两者都使用CGContextStrokePath()进行绘制。显然,UIBezierPath构造的路径比我使用CGPathAddCurveToPoint()创建的路径更有效。我想知道如何像UIBezierPath一样构造有效的路径。
Caleb

Answers:


154

您是正确的,因为UIBezierPath它只是Core Graphics的Objective-C包装器,因此将具有可比性。区别(以及性能差异的原因)是直接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];
}

仪器快照: 仪器快照显示相同的性能


6
感谢您抽出宝贵的时间对此进行研究并撰写如此清晰的说明。这确实是一个很好的答案。
Caleb

14
+1表示atari bruce lee图标...甚至可能是答案。
Grady Player

5
所以... 2倍的性能差异是cgcontext设置中的一项或多项-例如,可能类似:“ lineWidth为2.0的效果比lineWidth为1.0的效果差” ...?
2012年

2
FWIW我总是发现1.0的线宽是最快的-我的假设是因为斜切成为宽度> 1px的问题。
Mark Aufflick 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.