用CGBitmapContext绘制速度太慢

所以我在这个过程中有一个基本的绘图应用程序,可以让我画线。 我画到一个屏幕位图,然后在drawRect呈现图像。 它的工作方式太慢了,在用手指画出来之后更新约半秒钟。 我拿了代码,并从这个教程中调整它, http://www.youtube.com/watch?v= UfWeMIL-Nu8&feature=relmfu,你可以在评论中看到人们也说它太慢, t回应。

那我该如何加速呢? 还是有更好的方法来做到这一点? 任何指针将不胜感激。

下面是我的DrawView.m的代码。

 -(id)initWithCoder:(NSCoder *)aDecoder { if ((self=[super initWithCoder:aDecoder])) { [self setUpBuffer]; } return self; } -(void)setUpBuffer { CGContextRelease(offscreenBuffer); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); offscreenBuffer = CGBitmapContextCreate(NULL, self.bounds.size.width, self.bounds.size.height, 8, self.bounds.size.width*4, colorSpace, kCGImageAlphaPremultipliedLast); CGColorSpaceRelease(colorSpace); CGContextTranslateCTM(offscreenBuffer, 0, self.bounds.size.height); CGContextScaleCTM(offscreenBuffer, 1.0, -1.0); } -(void)drawToBuffer:(CGPoint)coordA :(CGPoint)coordB :(UIColor *)penColor :(int)thickness { CGContextBeginPath(offscreenBuffer); CGContextMoveToPoint(offscreenBuffer, coordA.x,coordA.y); CGContextAddLineToPoint(offscreenBuffer, coordB.x,coordB.y); CGContextSetLineWidth(offscreenBuffer, thickness); CGContextSetLineCap(offscreenBuffer, kCGLineCapRound); CGContextSetStrokeColorWithColor(offscreenBuffer, [penColor CGColor]); CGContextStrokePath(offscreenBuffer); } - (void)drawRect:(CGRect)rect { CGImageRef cgImage = CGBitmapContextCreateImage(offscreenBuffer); UIImage *image =[[UIImage alloc] initWithCGImage:cgImage]; CGImageRelease(cgImage); [image drawInRect:self.bounds]; } 

在模拟器上完美工作,但不是设备,我想这是处理器的速度。

我正在使用ARC。

我试图修复你的代码,但是因为你似乎只发布了一半的代码,所以我无法工作(复制+粘贴代码导致很多错误,更不用说开始性能调整了)。

但是有一些技巧可以用来极大地提高性能。

第一个,也许最明显的是-setNeedsDisplayInRect:而不是-setNeedsDisplay。 这意味着它只会重新绘制改变的小矩形。 对于具有1024 * 768 * 4像素的iPad 3来说,这是一个很大的工作。 将每帧减less到大约20 * 20或更less将大大提高性能。

 CGRect rect; rect.origin.x = minimum(coordA.x, coordB.x) - (thickness * 0.5); rect.size.width = (maximum(coordA.x, coordB.x) + (thickness * 0.5)) - rect.origin.x; rect.origin.y = minimum(coordA.y, coordB.y) - (thickness * 0.5); rect.size.height = (maximum(coordA.y, coordB.y) + (thickness * 0.5)) - rect.origin.y; [self setNeedsDisplayInRect:rect]; 

你可以做的另一个大的改进是只画CGPath这个当前的触摸(你这样做)。 然而,你然后在绘制rect绘制保存/caching的图像。 所以,再次重绘每一帧。 更好的方法是使绘制视图透明,然后使用UIImageView。 UIImageView是在iOS上显示图像的最佳方式。

 - DrawView (1 finger) -drawRect: - BackgroundView (the image of the old touches) -self.image 

绘制视图本身将只绘制当前的触摸只是每次更改的部分。 当用户举起他们的手指,你可以caching到UIImage,绘制在当前的背景/cachingUIImageView的图像,并将imageView.image设置为新的图像。

当组合图像时的最后一点涉及将2个全屏图像绘制到屏幕外的CGContext中,因此如果在主线程上完成,将导致滞后,而应该在后台线程中完成,然后将结果推回到主线程。

 * touch starts * - DrawView : draw current touch * touch ends * - 'background thread' : combine backgroundView.image and DrawView.drawRect * thread finished * send resulting UIImage to main queue and set backgroundView.image to it; Clear DrawView's current path that is now in the cache; 

所有这一切结合起来可以使一个非常stream畅的60fps的绘图应用程序。 然而,视图并没有像我们想要的那样快速更新,所以当移动graphics的时候,graphics看上去很乱。 这可以通过使用UIBezierPath而不是CGPaths来改进。

 CGPoint lastPoint = [touch previousLocationInView:self]; CGPoint mid = midPoint(currentPoint, lastPoint); -[UIBezierPath addQuadCurveToPoint:mid controlPoint:lastPoint]; 

这是因为它是缓慢的,因为你正在创build一个位图,并试图绘制每一帧。

你要求更好的方法吗? 你有没有看过iOS上的绘图应用程序的苹果示例代码 ? 如果你不喜欢,那么你总是可以使用提供CCRenderTexture类(和示例代码)的cocos2d 。

目前,您正在使用您已知的方法效率不高。

有了这种方法,我想你应该考虑使用后台线程的所有图像渲染和主线程的UI更新的唯一工作,即

 __block UIImage *__imageBuffer = nil; - (UIImage *)drawSomeImage { UIGraphicsBeginImageContext(self.bounds); // draw image with CoreGraphics UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; } - (void)updateUI { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // prepare image on background thread __imageBuffer = [self drawSomeImage]; dispatch_async(dispatch_get_main_queue(), ^{ // calling drawRect with prepared image [self setNeedsDisplay]; }); }); } - (void)drawRect { // draw image buffer on current context [__imageBuffer drawInRect:self.bounds]; } 

我省略了一些使优化更加清晰的细节。 更好切换到UIImageView 。 这样,你可以摆脱重要的- (void)drawDect方法,并在图像准备好的时候更新UIImageView图像属性。

使用CgLayer来caching你的path,阅读文档,最适合优化。

我做了这样的事情。 查看AppStore上的Pixelate应用程序。 为了绘制,我在我的代码中使用了瓷砖。 毕竟,当你画屏幕并绘制一些东西时,你需要重新绘制整个图像,这是一个非常繁重的操作。 如果你喜欢Pixelate的方式,我是这么做的:

1)分割我的形象在nxm瓷砖。 那是我可以改变这些价值观,并获得更大/更小的瓷砖。 在最坏的情况下(用户点击4个瓷砖的交点),您必须重新绘制这4个瓷砖。 不是整个图像。

2)制作一个三维matrix,其中存储了每个图块的像素信息。 因此, matrix[0][0][0]是第一个图块的第一个像素的红色值(每个像素具有RGB或RGBA值,取决于您是否使用pngs或jpg)。

3)获取用户按下的位置并计算需要修改的图块。

4)修改matrix中的值并更新需要更新的图块。

注意:这当然不是最好的select。 这只是一个select。 我提到它是因为我认为它接近你现在所拥有的。 它在iPhone 3GS上为我工作。 如果你的目标是> = iPhone 4,那么你应该不错。

问候,

乔治

不pipe你提出的方法效率太低,因为每次移动手指都要创build图像是不合适的。

如果您需要绘制其正确的path,则将CGMutablePathref作为成员variables,然后在绘制矩形中使用CGPath函数移动到指定的点。

更重要的是,在刷新视图时,调用setNeedsDisplayInRect只传递需要绘制的区域。 希望它能为你工作。