为什么 UIBezierPath 比 Core Graphics 路径快?

我当时正在尝试绘制路径,我注意到,至少在某些情况下,UIBezierPath 的性能超过了我所认为的核心图形等价物。下面的 -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 绘图?

26319 次浏览

You are correct in that UIBezierPath is simply an objective-c wrapper for Core Graphics, and therefore will perform comparably. The difference (and reason for your performance delta) is your CGContext state when drawing your CGPath directly is quite different to that setup by UIBezierPath. If you look at UIBezierPath, it has settings for:

  • lineWidth,
  • lineJoinStyle,
  • lineCapStyle,
  • miterLimit and
  • flatness

When examining the call (disassembly) to [path stroke], you will note that it configures the current graphic context based on those previous values before performing the CGContextStrokePath call. If you do the same prior to drawing your CGPath, it will perform the same:

- (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];
}

Snapshot from Instruments: Instruments snapshot showing equal performance