2011-06-13 5 views
81

Estaba jugando con las rutas de dibujo, y noté que en al menos algunos casos, UIBezierPath supera lo que pensé que sería un equivalente de Core Graphics. El siguiente método -drawRect: crea dos rutas: una UIBezierPath y una CGPath. Los caminos son idénticos, excepto por su ubicación, pero acariciar el CGPath toma aproximadamente el doble de tiempo que acariciar el UIBezierPath.¿Por qué UIBezierPath es más rápido que la ruta de Core Graphics?

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

Ambos caminos utilizan CGContextStrokePath(), por lo que crea métodos separados a un accidente cerebrovascular cada trayectoria de modo que pueda ver el tiempo utilizado por cada trayectoria de un instrumento. A continuación se muestran los resultados típicos (árbol de llamadas invertidas); se puede ver que lleva -strokeContext: 9,5 seg., mientras que -strokeUIBezierPath: tarda sólo 5 seg .:

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:] 

Parece que UIBezierPath es de alguna manera la optimización de la ruta que se crea, o estoy creando el CGPath de una manera ingenua. ¿Qué puedo hacer para acelerar mi dibujo de CGPath?

+2

1 que suena contra-intutive. –

+1

En general, he encontrado que CoreGraphics es muy lento al dibujar líneas, caminos, etcétera. No tengo idea de por qué, pero la mayoría de las veces tengo que ir a OpenGL o usar Cocos2D para un dibujo eficiente. Claro que entiendo que es más rápido, pero realmente no entiendo por qué CG es mucho más lento, considerando que debería usar OpenGL. – Accatyyc

+4

'UIBezierPath' es un contenedor alrededor de' CGPathRef'. ¿Qué pasa si ejecuta ambos, digamos, diez millones de veces, y luego toma el promedio, pero no utiliza instrumentos sino dos objetos 'NSDate' antes y después de las operaciones. –

Respuesta

142

Tiene usted razón en que UIBezierPath es simplemente una envoltura de Objective-C para el núcleo de gráficos, y por lo tanto realizar comparable. La diferencia (y la razón de su delta de rendimiento) es su estado CGContext cuando dibujar su CGPath directamente es bastante diferente a esa configuración por UIBezierPath. Si nos fijamos en UIBezierPath, tiene ajustes para:

  • lineWidth,
  • lineJoinStyle,
  • lineCapStyle,
  • miterLimit y
  • flatness

Al examinar la llamada (desmontaje) al [path stroke], usted Tenga en cuenta que configura el contexto gráfico actual en función de esos valores previos antes de realizar la llamada CGContextStrokePath. Si usted hace lo mismo antes de elaborar su CGPath, se llevará a cabo el mismo:

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

instantánea de Instrumentos: Instruments snapshot showing equal performance

+5

Gracias por tomarse el tiempo para analizar esto y escribir una explicación tan clara. Esta es realmente una gran respuesta. – Caleb

+1

Sin preocupaciones, me alegro de que ayudó –

+13

+1 para el icono de atari bruce lee ... y posiblemente por la respuesta. –

Cuestiones relacionadas