贝塞尔曲线绘制非常滞后

Bezier Curve drawing is very laggy

事情是这样的: 当我画的时候,贝塞尔曲线非常流畅。为了使平滑成为可能,我应用了制作控制点和终点的概念。但是,我找不到导致延迟的问题。

当我画画时,我检查了我的 CPU 使用情况,它在 5 秒内从大约 50% 增加到 90%。我确保当我完成绘图时,我的点会被擦除,同时会创建一个缓冲区来创建我所画内容的图像。

我的猜测是在touchesMoved中同时绘制了太多的点?肯定有一些点被填充,这对于程序来说可能太多了。

#import "SmoothedBIView.h"

@implementation SmoothedBIView
{
    UIBezierPath *path;
    UIImage *incrementalImage;
    CGPoint pts[5]; // need to keep track of the four points of a Bezier segment and the first control point of the next segment
    uint ctr;
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super initWithCoder:aDecoder])
    {
        [self setMultipleTouchEnabled:NO];
        [self setBackgroundColor:[UIColor whiteColor]];
        path = [UIBezierPath bezierPath];
        [path setLineWidth:2.0];
    }
    return self;

}
/* - (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self setMultipleTouchEnabled:NO];
        path = [UIBezierPath bezierPath];
        [path setLineWidth:2.0];
    }
    return self;
}
*/


 animation.
- (void)drawRect:(CGRect)rect
{
    [incrementalImage drawInRect:rect];
    [path stroke];
}


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    ctr = 0;
    UITouch *touch = [touches anyObject];
    pts[0] = [touch locationInView:self];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint p = [touch locationInView:self];
    ctr++;
    pts[ctr] = p;
    if (ctr == 4)
    {
        pts[3] = CGPointMake((pts[2].x + pts[4].x)/2.0, (pts[2].y + pts[4].y)/2.0); // move the endpoint to the middle of the line joining the second control point of the first Bezier segment and the first control point of the second Bezier segment

        [path moveToPoint:pts[0]];
        [path addCurveToPoint:pts[3] controlPoint1:pts[1] controlPoint2:pts[2]]; // add a cubic Bezier from pt[0] to pt[3], with control points pt[1] and pt[2]

        [self setNeedsDisplay];
        // replace points and get ready to handle the next segment
        pts[0] = pts[3];
        pts[1] = pts[4];
        ctr = 1;
    }

}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self drawBitmap];
    /*[self setNeedsDisplay]; */
    [path removeAllPoints];
    ctr = 0;
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self touchesEnded:touches withEvent:event];
}

- (void)drawBitmap
{
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, 0.0);

    if (!incrementalImage)
    {
        UIBezierPath *rectpath = [UIBezierPath bezierPathWithRect:self.bounds];
        [[UIColor whiteColor] setFill];
        [rectpath fill];
    }
    [incrementalImage drawAtPoint:CGPointZero];
    [[UIColor blackColor] setStroke];
    [path stroke];
    incrementalImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
}

@end

这里有几个问题:

  1. 如果您担心此算法的持续滞后,一个问题是您仅在计数器为 4 时才更新路径(并且您总是落后至少一分),这会夸大滞后性。您可以更频繁地更新路径,如此处所讨论的,,这是一个与您的非常相似的 Swift 实现。

    基本思路是,与其等到计数器达到 4,不如在计数器为 1 时画一条线,在计数器为 2 时画四边形曲线,在计数器为 3 时画三次曲线, 并在计数器为 4 时绘制修改后的三次曲线。这可以减少您的算法将遭受的​​一般延迟。

  2. 您可以通过使用预测触摸来减少感知到的延迟。它并没有解决问题(并且使算法稍微复杂化,因为您必须考虑当真正的触摸最终进入时退出先前预测的触摸的概念),但它可以使感知到的延迟进一步降低。请参阅 ,了解如何使用预测触摸的示例(结合 Catmull-Rom 和 Hermite 样条,另一种平滑算法)。对于 Swift 参考,我深表歉意,但如果您搜索 Objective-C 的预测触摸,我想您也会找到很多示例。

  3. 如果您担心贝塞尔曲线太长导致延迟,与其等到触摸结束再生成快照,不如在一定数量的触摸后进行,即使在一个手势的中间。这将防止您的贝塞尔曲线变得太长以至于您开始遇到性能问题。是的,您不想在绘图手势中间做一些计算密集型操作,但您必须在某个时候画一条线(没有双关语意)。

  4. 对于贝塞尔曲线路径使用 CAShapeLayer 而不是自己绘制存在争议。我看到它表明这比简单的 drawRect 实现更优化,但我承认我从未对其进行基准测试。

一个简单的性能胜利是使用 setNeedsDisplayInRect: 而不是 setNeedsDisplay。您当前正在重绘整个视图,而不仅仅是已更改的区域。