iOS上的简单多点触控绘图应用程序:太慢(因为drawRect()不是添加剂?)

时间:2012-04-17 10:49:05

标签: objective-c ios core-graphics quartz-2d

我正在使用Quartz 2D制作一款简单的多点触控绘图iPad游戏。游戏要求我每隔1/30秒在手指位置画一个新笔画。

据我所知,基本上没有办法让drawRect()在每次调用时都不清除上下文(self.clearsContextBeforeDrawing = NO;不起作用),所以我的解决方案是创建一个后台缓冲区位图(或图层,我可以使用两者),每个手指每次迭代都会将每个新的小笔划绘制到该缓冲区中,然后每次调用drawRect()时将缓冲区复制到屏幕中。换句话说:

backlayer = CGLayerCreateWithContext(context, CGSizeMake(W, H), NULL);
offctx = CGLayerGetContext (backlayer); 

然后

- (void)drawRect:(CGRect)rect
{

    CGContextRef context = UIGraphicsGetCurrentContext();

    //code here to draw small strokes from old finger position to new one

    CGContextDrawLayerInRect(context, [self bounds], backlayer);
}

当我在iPad 2上进行测试时,这没有问题,但是昨天我注意到在新的iPad 3上相同的代码运行得慢得多。性能非常糟糕,我的游戏速度从30FPS减慢到大约5左右,可能是由于视网膜显示较大。我有同样的问题,如果我使用我创建的单独的CGBitmapContext,然后每次迭代我从它创建一个ImageRef并使用CGContextDrawImage绘制它。

我可以采取什么方法来解决这个问题?似乎我必须在每次迭代时重新绘制所有内容,因为它甚至不能通过一个小的矩形来绘制更改的内容(因为每次迭代都需要为每个手指设置几个矩形)

谢谢

1 个答案:

答案 0 :(得分:2)

我设法解决了这个问题:

我创建了一个新的UIView子类标头和实现文件:

@interface fingerView : UIView {
}

然后在我的主视图中,在标题中我声明了其中5个视图:

fingerView* fview[5];

在我的主视图实现中,我创建了这个实例的5个视图,每个视图分别对应一个。此外,必须确保制作它们,为每个它们启用多点触控,并确保将clearsContextBeforeDrawing设置为NO,因为我们将一次更新每个中的微小rects,我们不希望系统清除我们的工作

for(int i=0;i<5;i++) {
        fview[i] = [[pView alloc] initWithFrame:topFrame];
        [self addSubview: fview[i]];
        [self sendSubviewToBack: fview[i]];
        fview[i].opaque= NO;
        fview[i].clearsContextBeforeDrawing = NO;
        fview[i].multipleTouchEnabled = YES;
}

现在,在每个手指视图中,保留一个大数组(我使用一个简单的数组,比如10,000长)的手指所绘制的x和y位置。每当手指移动时,主视图就会检测到它,然后调用[fview [i] updatePos(newx,newy)],关键的是,我们将命令视图仅在这些坐标周围更新一小部分:

[fview[i] setNeedsDisplayInRect: fingerRect];

其中fingerRect是一个以(newx,newy)为中心的小矩形。在每个手指视图的drawRect方法内,

- (void)drawRect:(CGRect)rect
{
    if (movep==0) return;

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetRGBStrokeColor(context, r, g, b, 1);
    CGContextSetLineWidth(context, linewidth);

    //paint finger
    CGContextBeginPath(context);
    CGFloat slack= 15;
    CGFloat minx= CGRectGetMinX(rect)-slack;
    CGFloat maxx= CGRectGetMaxX(rect)+slack;
    CGFloat miny= CGRectGetMinY(rect)-slack;
    CGFloat maxy= CGRectGetMaxY(rect)+slack;    
    bool drawing = NO;
    for(int i=0;i<movep;i++) {
        CGFloat xx= x[i];
        CGFloat yy= y[i];
        if(xx>minx && xx<maxx && yy>miny && yy<maxy) {

            if(drawing) {

                // continue line
                CGContextAddLineToPoint(context, xx, yy);
                CGContextMoveToPoint(context, xx, yy);

            } else {

                // start drawing
                CGContextMoveToPoint(context, xx, yy);
                drawing= YES;
            }

        } else {
            drawing= NO;  
        }
    }

    CGContextStrokePath(context);

并且,正如我所提到的那样

- (void)updatePos: (CGFloat)xnew: (CGFloat) ynew
{
    x[movep]= xnew;
    y[movep]= ynew;
    movep= movep+1;

希望你能弄明白这是如何运作的。每个视图都会查看已经修改过的这个矩形,并检查围绕该矩形的所有手指位置,并仅绘制那些。这将归结为非常少的笔画,因此整个代码运行得非常快。

总的来说,UIViews是非常优化的。尽可能地尝试制作一大堆它们,如果有的话,只在本地更新它们,并让Apple的魔法将它们融合在一起。