1. ホーム
  2. iphone

[解決済み] なぜUIBezierPathはCore Graphicsパスより高速なのですか?

2023-03-16 04:49:52

質問

私はパスの描画で遊んでいて、少なくともいくつかのケースで、UIBezierPath が Core Graphics の同等品と思われるものより優れていることに気づきました。その -drawRect: メソッドは、UIBezierPath と CGPath の 2 つのパスを作成します。これらのパスは位置以外は同じですが、CGPath をストロークすると、UIBezierPath をストロークするよりも約 2 倍の時間がかかります。

- (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() を使用しているので、それぞれのパスが使用する時間を Instruments で見ることができるように、それぞれのパスをストロークする別々のメソッドを作成しました。以下は典型的な結果です (コール ツリーを反転したもの)、以下のことがわかります。 -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 は単に Core Graphics の objective-c ラッパーであり、したがって同等のパフォーマンスを発揮します。 違い (およびパフォーマンスの差の理由) は 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];
}

インストゥルメントからのスナップショット。