I am trying to use CAShapeLayer
to mask a CALayer
in my iOS
app as it takes a fraction of the CPU time
to mask an image vs manually masking one in a bitmap context
;
我正在尝试使用CAShapeLayer来屏蔽我的iOS应用程序中的CALayer,因为它需要一小部分CPU时间来屏蔽图像,而不是在位图上下文中手动屏蔽图像;
When I have several dozen or more images layered over each other, the CAShapeLayer
masked UIImageView
is slow to move to my touch.
当我有几十个或更多图像叠加在一起时,CAShapeLayer屏蔽UIImageView移动到我的触摸很慢。
Here is some example code:
这是一些示例代码:
UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"SomeImage.jpg" ofType:nil]];
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddEllipseInRect(path, NULL, CGRectMake(0.f, 0.f, image.size.width * .25, image.size.height * .25));
for (int i = 0; i < 200; i++) {
SLTUIImageView *imageView = [[SLTUIImageView alloc]initWithImage:image];
imageView.frame = CGRectMake(arc4random_uniform(CGRectGetWidth(self.view.bounds)), arc4random_uniform(CGRectGetHeight(self.view.bounds)), image.size.width * .25, image.size.height * .25);
CAShapeLayer *shape = [CAShapeLayer layer];
shape.path = path;
imageView.layer.mask = shape;
[self.view addSubview:imageView];
[imageView release];
}
CGPathRelease(path);
With the above code, imageView
is very laggy. However, it reacts instantly if I mask it manually in a bitmap context
:
使用上面的代码,imageView非常滞后。但是,如果我在位图上下文中手动屏蔽它,它会立即做出反应:
UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"3.0-Pad-Classic0.jpg" ofType:nil]];
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddEllipseInRect(path, NULL, CGRectMake(0.f, 0.f, image.size.width * .25, image.size.height * .25));
for (int i = 0; i < 200; i++) {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(image.size.width * .25, image.size.height * .25), NO, [[UIScreen mainScreen]scale]);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextAddPath(ctx, path);
CGContextClip(ctx);
[image drawInRect:CGRectMake(-(image.size.width * .25), -(image.size.height * .25), image.size.width, image.size.height)];
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
SLTUIImageView *imageView = [[SLTUIImageView alloc]initWithImage:finalImage];
imageView.frame = CGRectMake(arc4random_uniform(CGRectGetWidth(self.view.bounds)), arc4random_uniform(CGRectGetHeight(self.view.bounds)), finalImage.size.width, finalImage.size.height);
[self.view addSubview:imageView];
[imageView release];
}
CGPathRelease(path);
By the way, here is the code to SLTUIImageView
, it's just a simple subclass of UIImageView
that responds to touches (for anyone who was wondering):
顺便说一句,这是SLTUIImageView的代码,它只是UIImageView的一个简单子类,它响应触摸(对于任何想知道的人):
-(id)initWithImage:(UIImage *)image{
self = [super initWithImage:image];
if (self) {
self.userInteractionEnabled = YES;
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[self.superview bringSubviewToFront:self];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
self.center = [touch locationInView:self.superview];
}
Is it possible to somehow optimize how the CAShapeLayer
is masking the UIImageView
so that the performance is improved? I have tried to find out where the bottle-neck is using the Time Profiler
in Instruments
, but I can't tell exactly what is causing it.
是否有可能以某种方式优化CAShapeLayer如何屏蔽UIImageView以提高性能?我试图找出瓶颈在仪器中使用Time Profiler的位置,但我无法确切地知道是什么导致它。
I have tried setting shouldRasterize
to YES
on both layer
and on layer.mask
but neither seem to have any effect. I'm not sure what to do.
我已经尝试在两层和layer.mask上将shouldRasterize设置为YES,但似乎都没有任何效果。我不知道该怎么做。
Edit:
编辑:
I have done more testing and find that if I use just a regular CALayer
to mask another CALayer (layer.mask = someOtherLayer)
I have the same performance issues. It seems that the problem isn't specific to CAShapeLayer
—rather it is specific to the mask
property of CALayer
.
我做了更多测试,发现如果我只使用常规CALayer来屏蔽另一个CALayer(layer.mask = someOtherLayer),我会遇到相同的性能问题。似乎问题不是特定于CAShapeLayer的 - 而是特定于CALayer的mask属性。
Edit 2:
编辑2:
So after learning more about using the Core Animation tool
in Instruments
, I learned that the view is being rendered offscreen each time it moves. Setting shouldRaster
to YES
when the touch begins and turning it off when the touch ends makes the view stay green (thus keeping the cache) in instruments
, but performance is still terrible. I believe this is because even though the view is being cached, if it isn't opaque, than it still has to be re-rendered with each frame.
因此,在了解了有关在Instruments中使用Core Animation工具的更多信息后,我了解到视图每次移动时都会在屏幕外渲染。触摸开始时设置shouldRaster为YES,触摸结束时将其设置为关闭使视图保持绿色(从而保持缓存),但性能仍然很差。我相信这是因为即使视图被缓存,如果它不是不透明的,那么它仍然必须用每个帧重新渲染。
One thing to emphasize is that if there are only a few views being masked (say even around ten) the performance is pretty good. However, when you increase that to 100 or more
, the performance lags. I imagine this is because when one moves over the others, they all have to be re-rendered.
需要强调的一点是,如果只有少数视图被屏蔽(甚至大约十个),那么性能非常好。但是,当您将其增加到100或更多时,性能会滞后。我想这是因为当一个人移过其他人时,他们都必须重新渲染。
My conclusion is this, I have one of two options.
我的结论是,我有两种选择之一。
First, there must be someway to permanently mask a view (render it once and call it good). I know this can be done via the graphic or bitmap context route as I show in my example code, but when a layer masks its view, it happens instantly. When I do it in a bitmap context as shown, it is quite slow (as in it almost can't even be compared how much slower it is).
首先,必须有一些永久屏蔽视图(渲染一次并称之为好)。我知道这可以通过我在示例代码中显示的图形或位图上下文路由来完成,但是当图层屏蔽其视图时,它会立即发生。当我在如图所示的位图上下文中执行时,它非常慢(因为它几乎甚至无法比较它的速度有多慢)。
Second, there must be some faster
way to do it via the bitmap context route. If there is an expert in masking images or views, their help would be very much appreciated.
其次,必须有一些更快的方法通过位图上下文路由来完成它。如果有专家屏蔽图像或视图,他们的帮助将非常感谢。
2 个解决方案
#1
1
You've gotten pretty far along and I believe are almost to a solution. What I would do is simply an extension of what you've already tried. Since you say many of these layers are "ending up" in final positions that remain constant relative to the other layers, and the mask.. So simply render all those "finished" layers to a single bitmap context. That way, every time you write out a layer to that single context, you'll have one less layer to worry about that is slowing down the animation/rendering process.
你已经走得很远,我相信几乎是一个解决方案。我要做的只是你已经尝试过的扩展。因为你说很多这些层在最终位置“结束”,相对于其他层和掩码保持不变。所以简单地将所有那些“完成”的层渲染到单个位图上下文。这样,每次你向一个单独的上下文写一个图层时,你就会有一个较少的图层来担心这会减慢动画/渲染过程的速度。
#2
0
Quartz (drawRect:) is slower than CoreAnimation for many reasons: CALayer vs CGContext drawRect vs CALayer. But it is necessary to use it correctly.
Quartz(drawRect :)比CoreAnimation慢,原因有很多:CALayer vs CGContext drawRect vs CALayer。但是有必要正确使用它。
In the documentation you can see some advices. ImprovingCoreAnimationPerformance
在文档中,您可以看到一些建议。 ImprovingCoreAnimationPerformance
If you want a hight performance, maybe you can try using AsyncDisplayKit. This framework allows to create smooth and responsive apps.
如果你想要一个高性能,也许你可以尝试使用AsyncDisplayKit。该框架允许创建流畅和响应迅速的应用程序。
#1
1
You've gotten pretty far along and I believe are almost to a solution. What I would do is simply an extension of what you've already tried. Since you say many of these layers are "ending up" in final positions that remain constant relative to the other layers, and the mask.. So simply render all those "finished" layers to a single bitmap context. That way, every time you write out a layer to that single context, you'll have one less layer to worry about that is slowing down the animation/rendering process.
你已经走得很远,我相信几乎是一个解决方案。我要做的只是你已经尝试过的扩展。因为你说很多这些层在最终位置“结束”,相对于其他层和掩码保持不变。所以简单地将所有那些“完成”的层渲染到单个位图上下文。这样,每次你向一个单独的上下文写一个图层时,你就会有一个较少的图层来担心这会减慢动画/渲染过程的速度。
#2
0
Quartz (drawRect:) is slower than CoreAnimation for many reasons: CALayer vs CGContext drawRect vs CALayer. But it is necessary to use it correctly.
Quartz(drawRect :)比CoreAnimation慢,原因有很多:CALayer vs CGContext drawRect vs CALayer。但是有必要正确使用它。
In the documentation you can see some advices. ImprovingCoreAnimationPerformance
在文档中,您可以看到一些建议。 ImprovingCoreAnimationPerformance
If you want a hight performance, maybe you can try using AsyncDisplayKit. This framework allows to create smooth and responsive apps.
如果你想要一个高性能,也许你可以尝试使用AsyncDisplayKit。该框架允许创建流畅和响应迅速的应用程序。