webentwicklung-frage-antwort-db.com.de

Warum ist UIBezierPath schneller als der Core Graphics-Pfad?

Ich habe mit dem Zeichnen von Pfaden herumgespielt und festgestellt, dass UIBezierPath in einigen Fällen besser ist als das, was ich für ein Core Graphics-Äquivalent gehalten habe. Das -drawRect: Methode unten erstellt zwei Pfade: einen UIBezierPath und einen CGPath. Die Pfade sind bis auf die Position identisch, das Zeichnen des CGPath dauert jedoch ungefähr doppelt so lange wie das Zeichnen des 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];
}

Beide Pfade verwenden CGContextStrokePath (). Daher habe ich separate Methoden zum Strichzeichnen der einzelnen Pfade erstellt, sodass die von den einzelnen Pfaden in Instruments verwendete Zeit angezeigt wird. Nachfolgend finden Sie typische Ergebnisse (Anrufbaum invertiert). Sie können sehen, dass -strokeContext: dauert 9,5 Sek., während -strokeUIBezierPath: dauert nur 5 sek .:

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

Es sieht so aus, als würde UIBezierPath den Pfad, den es erstellt, irgendwie optimieren, oder ich erstelle den CGPath auf naive Weise. Was kann ich tun, um mein CGPath-Zeichnen zu beschleunigen?

86
Caleb

Sie haben Recht, dass UIBezierPath einfach ein Objective-C-Wrapper für Core Graphics ist und daher eine vergleichbare Leistung erbringt. Der Unterschied (und der Grund für Ihr Leistungsdelta) ist Ihr CGContext - Status, wenn Sie Ihr CGPath direkt zeichnen, unterscheidet sich erheblich von dem von UIBezierPath. Wenn Sie sich UIBezierPath ansehen, hat es Einstellungen für:

  • lineWidth,
  • lineJoinStyle,
  • lineCapStyle,
  • miterLimit und
  • flatness

Bei der Prüfung des Aufrufs (Demontage) an [path stroke], Sie werden feststellen, dass der aktuelle Grafikkontext basierend auf diesen vorherigen Werten konfiguriert wird, bevor der Aufruf CGContextStrokePath ausgeführt wird. Wenn Sie vor dem Zeichnen Ihres CGPath dasselbe tun, wird dasselbe ausgeführt:

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

Schnappschuss von Instrumenten: Instruments snapshot showing equal performance

149
Stuart Carnie