UIBezierPath neden Core Graphics yolundan daha hızlıdır?


90

Çizim yollarıyla oynuyordum ve en azından bazı durumlarda UIBezierPath'in Core Graphics eşdeğeri olacağını düşündüğümden daha iyi performans gösterdiğini fark ettim. Aşağıdaki -drawRect:yöntem iki yol oluşturur: bir UIBezierPath ve bir CGPath. Yollar, konumları dışında aynıdır, ancak CGPath'i okşamak, UIBezierPath'i okşamaktan kabaca iki kat daha uzun sürer.

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

Her iki yol da CGContextStrokePath () kullanıyor, bu yüzden her bir yol tarafından kullanılan zamanı görebilmem için her yolu konturlamak için ayrı yöntemler yarattım. Aşağıda tipik sonuçlar verilmiştir (çağrı ağacı ters çevrilmiştir); -strokeContext:9,5 saniye sürdüğünü görebilirsiniz .-strokeUIBezierPath: sadece 5 sn sürer .:

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

Görünüşe göre UIBezierPath oluşturduğu yolu bir şekilde optimize ediyor veya CGPath'i saf bir şekilde oluşturuyorum. CGPath çizimimi hızlandırmak için ne yapabilirim?


2
Kulağa ters gelen +1.
Grady Player

1
Genelde CoreGraphics'in çizgi, yol vb. Çizerken çok yavaş olduğunu gördüm. Neden olduğuna dair hiçbir fikrim yok ama verimli çizim için çoğunlukla OpenGL'ye gitmem veya Cocos2D kullanmam gerekiyor. Elbette bunun daha hızlı olduğunu anlıyorum, ancak OpenGL'nin kendisini kullanması gerektiğini düşünürsek, CG'nin neden bu kadar yavaş olduğunu gerçekten anlamıyorum.
Accatyyc

4
UIBezierPathetrafta bir sarmalayıcıdır CGPathRef. Ya her ikisini de on milyon kez çalıştırırsanız, o zaman ortalamayı alın, ancak Enstrümanlar NSDateyerine operasyonlardan önce ve sonra iki nesne kullanın .

1
@WTP, sonuçlar cihazda ve simülatörde tutarlıdır ve -drawRect:birkaç düzine kez mi yoksa birkaç yüz kez mi çağrıldığını değiştirmez . Simülatörde 80000'e kadar eğri segmentiyle denedim (cihaz için çok fazla). Sonuçlar her zaman yaklaşık olarak aynıdır: Her ikisi de çizim için CGContextStrokePath () kullansa da, CGPath UIBezierPath'in iki katı kadar uzun sürer. UIBezierPath'in oluşturduğu yolun bir şekilde CGPathAddCurveToPoint () ile oluşturduğum yoltan daha verimli olduğu açık görünüyor. UIBezierPath'in yaptığı gibi verimli yolları nasıl inşa edeceğimi bilmek istiyorum.
Caleb

Yanıtlar:


156

Haklısınız, bu UIBezierPathsadece Core Graphics için bir amaç-c sarmalayıcıdır ve bu nedenle karşılaştırılabilir bir performans sergileyecektir. Fark (ve performans deltanızın nedeni), doğrudan CGContextçizim yaparken durumunuzdur, CGPathbu kurulumdan oldukça farklıdır UIBezierPath. Bakarsanız UIBezierPath, aşağıdakiler için ayarları vardır:

  • lineWidth,
  • lineJoinStyle,
  • lineCapStyle,
  • miterLimit ve
  • flatness

Çağrıyı incelerken (demontaj) [path stroke], CGContextStrokePatharamayı gerçekleştirmeden önce mevcut grafik bağlamını bu önceki değerlere göre yapılandırdığını fark edeceksiniz . CGPath'inizi çizmeden önce aynısını yaparsanız, aynı işlemi gerçekleştirecektir:

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

Cihazlardan Anlık Görüntü: Eşit performans gösteren enstrüman anlık görüntüsü


6
Bu konuyu incelemeye ve bu kadar net bir açıklama yazmaya zaman ayırdığınız için teşekkür ederiz. Bu gerçekten harika bir cevap.
Caleb

14
Atari bruce lee simgesi için +1 ... ve muhtemelen cevap için.
Grady Oyuncu

5
Yani ... 2x performans farkı cgcontext ayarlarından biri veya daha fazlasıydı - örneğin, "lineWidth of 2.0, lineWidth of 1.0'dan daha kötü performans gösteriyor" ...?
Adam

2
FWIW Her zaman 1.0'ın en hızlı çizgi genişliğini buldum - benim varsayımım, azaltmanın> 1px genişlikleri için bir sorun haline gelmesidir.
Mark Aufflick
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.