87 votes

Pourquoi est-il UIBezierPath plus vite que le chemin d’accès de Core Graphics ?

J'ai été jouer avec le dessin des chemins, et j'ai remarqué que dans certains cas au moins, UIBezierPath surpasse ce que je pensais serait un Core Graphique équivalent. L' -drawRect: méthode ci-dessous crée deux voies: l'une UIBezierPath, et un CGPath. Les chemins d'accès sont identiques à l'exception de leurs emplacements, mais en se caressant le CGPath prend à peu près deux fois plus long que caresser le 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];
}

Les deux chemins de l'utilisation CGContextStrokePath(), j'ai donc créé méthodes distinctes pour l'avc chaque chemin, afin que je puisse voir le temps utilisé par chaque chemin dans les Instruments. Ci-dessous est typique des résultats (de l'arbre d'appel inversé); vous pouvez voir que -strokeContext: prend 9.5 sec., alors qu' -strokeUIBezierPath: ne prend que 5 secondes.:

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

Il ressemble à UIBezierPath est en quelque sorte l'optimisation de la trajectoire qu'elle crée, ou je suis de la création de la CGPath dans un naïf. Que puis-je faire pour augmenter la vitesse de mon CGPath dessin?

151voto

Stuart Carnie Points 2961

Vous avez raison UIBezierPath est tout simplement un objectif-c wrapper pour les Graphiques de Base, et par conséquent un rendement comparable. La différence (et la raison de votre performance delta) est votre CGContext de l'etat lors de l'élaboration de votre CGPath directement est assez différente de celle de l'installation par UIBezierPath. Si vous regardez UIBezierPath, il a des paramètres pour:

  • lineWidth,
  • lineJoinStyle,
  • lineCapStyle,
  • miterLimit et
  • flatness

Lors de l'examen de l'appel (démontage) [path stroke], vous allez noter qu'il configure le graphique actuel contexte basé sur ces valeurs précédentes avant d'effectuer l' CGContextStrokePath appel. Si vous faites de même avant la rédaction de votre CGPath, il va effectuer la même:

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

Instantané à partir d'Instruments: Instruments snapshot showing equal performance

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X