UIBezierPath가 Core Graphics 경로보다 빠른 이유는 무엇입니까?


90

그리기 경로를 가지고 놀았는데, 적어도 어떤 경우에는 UIBezierPath가 Core Graphics와 동등하다고 생각했던 것보다 성능이 뛰어납니다. -drawRect:하나 UIBezierPath, 하나 CGPath : 방법에 대하여 두 가지의 경로를 생성한다. 경로는 위치를 제외하고 동일하지만 CGPath를 입력하는 데 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];
}

두 경로 모두 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 그리기 속도를 높이려면 어떻게해야합니까?


2
반 직관적으로 들리는 +1.
Grady Player

1
나는 일반적으로 선, 경로 등을 그릴 때 CoreGraphics가 매우 느리다는 것을 발견했습니다. 이유는 모르겠지만 대부분 OpenGL로 이동하거나 효율적인 그리기를 위해 Cocos2D를 사용해야합니다. 확실히 나는 그것이 더 빠르다는 것을 이해하지만 CG가 OpenGL 자체를 사용해야한다는 점을 고려할 때 CG가 왜 그렇게 훨씬 느린 지 실제로 이해하지 못합니다.
Accatyyc

4
UIBezierPath주위에 래퍼 CGPathRef입니다. 두 가지를 모두 실행한다고 가정 해 봅시다. 평균을 취하되 Instruments를 사용하지 않고 NSDate작업 전후에 두 개의 개체를 사용하면 어떨까요?

1
@WTP, 결과는 장치와 시뮬레이터에서 일관되며 -drawRect:수십 번 호출 되든 수백 번 호출 되든 변경되지 않습니다 . 시뮬레이터에서 최대 80000 개의 곡선 세그먼트로 시도했습니다 (장치에 비해 너무 많음). 결과는 항상 거의 동일합니다. 둘 다 CGContextStrokePath ()를 사용하여 그림을 그리는 경우에도 CGPath는 UIBezierPath보다 약 2 배 더 오래 걸립니다. UIBezierPath가 구성하는 경로가 CGPathAddCurveToPoint ()로 만든 경로보다 어떻게 든 더 효율적인 것 같습니다. UIBezierPath처럼 효율적인 경로를 구성하는 방법을 알고 싶습니다.
Caleb

답변:


154

UIBezierPathCore Graphics에 대한 단순히 Objective-c 래퍼라는 점에서 정확 하므로 성능이 비슷합니다. 차이점 (및 성능 델타 이유)은 직접 CGContext그릴 때 의 상태 CGPathUIBezierPath. 을 보면 다음에 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];
}

악기의 스냅 샷 : 동일한 성능을 보여주는 계측기 스냅 샷


6
시간을내어 조사하고 명확한 설명을 작성해 주셔서 감사합니다. 이것은 정말 좋은 대답입니다.
Caleb

14
아타리 브루스 리 아이콘에 +1 ... 그리고 아마도 대답에 대해.
Grady Player

5
그래서 ... 2 배의 성능 차이는 cgcontext 설정 중 하나 이상이었습니다.
Adam

2
FWIW 저는 항상 1.0의 선 너비가 가장 빠른 것으로 나타났습니다. 제 가정은 마이 터링이 너비> 1px에 대해 문제가되기 때문입니다.
Mark Aufflick 2013 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.