所以我在这个过程中有一个基本的绘图应用程序,允许我绘制线条。我绘制到屏幕外的位图,然后在drawRect
中显示图像。它工作但速度太慢,用你的手指画出来后更新大约半秒钟。我从这个教程http://www.youtube.com/watch?v=UfWeMIL-Nu8&feature=relmfu中获取了代码并对其进行了调整,正如您在评论中所看到的那样,人们也说它太慢但是这个人没有回应。
那么我怎样才能加快速度呢?或者有更好的方法吗?任何指针都将受到赞赏。
继承我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。
答案 0 :(得分:6)
我试图修复你的代码,但是因为你似乎只发布了一半的代码我无法使它工作(复制+粘贴代码导致很多错误,更不用说启动性能调整了)。
但是,您可以使用一些提示来大幅提高性能。
第一个,也可能是最引人注目的是-setNeedsDisplayInRect:而不是-setNeedsDisplay。这意味着它只会重绘已更改的小矩形。对于具有1024 * 768 * 4像素的iPad 3来说,这是一项很多工作。将每帧减少到大约20 * 20或更低将大幅提高性能。
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中绘制已保存/缓存的图像。所以,再次,每帧重绘一次。更好的方法是使绘图视图透明,然后使用后面的UIImageView。 UIImageView是在iOS上显示图像的最佳方式。
- DrawView (1 finger)
-drawRect:
- BackgroundView (the image of the old touches)
-self.image
绘制视图本身只会绘制当前触摸仅每次更改的部分。当用户抬起手指时,您可以将其缓存到UIImage,在当前背景/缓存UIImageView的图像上绘制它,并将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;
所有这些组合可以制作一个非常流畅的60fps绘图应用程序。但是,视图不会像我们希望的那样快速更新,因此在更快地移动图形时绘图看起来是锯齿状的。这可以通过使用UIBezierPath而不是CGPath来改进。
CGPoint lastPoint = [touch previousLocationInView:self];
CGPoint mid = midPoint(currentPoint, lastPoint);
-[UIBezierPath addQuadCurveToPoint:mid controlPoint:lastPoint];
答案 1 :(得分:5)
它很慢的原因是因为每个帧都在创建一个位图并试图绘制它。
你问过更好的方法吗?你看过the apple sample code for a drawing app on iOS了吗?如果你不喜欢,那么你总是可以使用提供CCRenderTexture类的cocos2d(以及示例代码)。
目前,您正在使用一种您已经知道效率不高的方法。
答案 2 :(得分:2)
通过这种方法,我认为你应该考虑将后台线程用于图像渲染的所有艰苦工作,并且仅针对UI更新使用主线程,i。即
__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
的图像属性。
答案 3 :(得分:1)
我觉得你需要改变你的逻辑。借助此链接,您可以获得一些非常好的主意 http://devmag.org.za/2011/04/05/bzier-curves-a-tutorial/ 如果你认为你没有时间去理解那么你可以直接使用这段代码https://github.com/levinunnink/Smooth-Line-View :)我跳这会对你有所帮助。
答案 4 :(得分:0)
使用CgLayer缓存路径,阅读文档,最适合优化。
答案 5 :(得分:0)
无论您提出的方法效率太低,因为每次移动手指时创建图像都是不合适的。
如果它只需要绘制路径,那么将CGMutablePathref
作为成员变量,
并在绘制rect中使用CGPath
函数移动到指定的点。
更重要的是,在刷新视图时,调用setNeedsDisplayInRect
仅传递您需要绘制的区域。希望它对你有用。
答案 6 :(得分:0)
我做了一件完全像这样的事情。查看AppStore上的Pixelate应用程序。为了绘制,我在代码中使用了tile。毕竟,当您点击屏幕并绘制一些东西时,您需要重新绘制整个图像,这是一个非常繁重的操作。如果你喜欢Pixelate的移动方式,我就是这样做的:
1)将我的图像分割成n x m个图块。那是我可以改变这些值并获得更大/更小的瓷砖。在最坏的情况下(用户点击4个图块的交叉点),您必须重新绘制这4个图块。不是整个图像。
2)制作一个三维矩阵,其中我正在存储每个图块的像素信息。因此matrix[0][0][0]
是第一个图块的第一个像素的红色值(每个像素具有RGB或RGBA值,具体取决于您使用的是png还是jpgs)。
3)获取用户按下的位置并计算需要修改的图块。
4)修改矩阵中的值并更新需要更新的图块。
注意:这肯定不是最好的选择。这只是一个选择。我之所以提到它是因为我认为它与你现在拥有的很接近。它适用于iPhone 3GS。如果你的目标是> = iPhone 4,那么你应该还不错。
此致
乔治