Я играл с контурами рисования и заметил, что по крайней мере в некоторых случаях 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?
UIBezierPath
это обертка вокругCGPathRef
. Что, если вы запустите оба, скажем, десять миллионов раз, затем возьмете среднее значение, но не используете инструменты, а дваNSDate
объекта до и после операций.-drawRect:
вызове несколько десятков раз или нескольких сотен. Я пробовал это с целых 80000 сегментов кривой на симуляторе (слишком много для устройства). Результаты всегда примерно одинаковы: CGPath занимает примерно в два раза больше времени, чем UIBezierPath, хотя оба используют CGContextStrokePath () для рисования. Кажется очевидным, что путь, построенный UIBezierPath, в какой-то мере более эффективен, чем тот, который я создаю с помощью CGPathAddCurveToPoint (). Я хотел бы знать, как строить эффективные пути, как это делает UIBezierPath.Ответы:
Вы правы в том, что
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]; }
Снимок с инструментов:
источник