在CATiledLayer上高效绘制CGPath

8

我如何有效地在CATiledLayer上绘制一个CGPath?目前,我是通过以下方式检查瓦片的边界框是否与路径的边界框相交:

-(void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context {
    CGRect boundingBox = CGPathGetPathBoundingBox(drawPath);
    CGRect rect = CGContextGetClipBoundingBox(context);

    if( !CGRectIntersectsRect(boundingBox, rect) )
        return;

    // Draw path...
}

这种方式效率不高,因为drawLayer:inContext:会被多个线程多次调用,导致路径被重复绘制多次。有更好、更高效的方法吗?

你可能需要查看为什么你的drawLayer:inContext:被调用了这么多次...我刚刚检查了一下,在一个新项目中只有一个视图,它只会被调用一次。 - livingtech
1
@livingtech: CATiledLayer的整个意义在于它由多个独立绘制的瓷砖组成,并且在多个线程上绘制。 -drawLayer:inContext:确实应该被调用多次,假设图层的尺寸大于单个瓷砖。 - Andrew Madsen
我遇到了类似的问题 - 我不知道如何最好地显示一个大路径。我正在使用CATiledLayer,但不确定这是否是最佳方法。根据我目前所读的内容,平铺对图像比矢量渲染的CA/CG内容更好。这是真的吗?希望有人能回答 :-) - GWed
@AndrewMadsen 对的,有时候我比较慢。 - livingtech
2个回答

5
最简单的方法是将曲线绘制到一个大图像中,然后平铺该图像。但如果你要平铺,那么很可能意味着该图像太大了,或者你本来就应该首先绘制路径,对吧?
因此,您可能需要分割您的路径。最简单的方法是使用CGPathApply逐个元素拆分它。对于每个元素,您可以检查其边界框,并确定该元素是否落在您的边界内。如果没有,则只需跟踪上一个端点。如果是,则移动到您看到的上一个端点,并将该元素添加到此瓷砖的新路径中。完成后,每个瓷砖都会绘制自己的路径。
从技术上讲,您将在此绘制超出您边界的内容(例如延伸到瓷砖之外的线),但这比听起来要便宜得多。核心图形将非常容易地裁剪单个元素。目标是避免计算根本不在您边界框内的元素。
一定要缓存生成的路径。您不需要为每个瓷砖计算路径;只需绘制您要绘制的即可。但是避免每次绘制瓷砖时重新计算它。每当数据发生更改时,清除缓存。如果有大量的瓷砖,则还可以使用NSCache来进一步优化。

看起来是个不错的答案,Rob能否展示一些代码来更详细地解释你的方法? - GWed
我没有任何公开可用的东西。作为我的贝塞尔曲线系列的一部分,我可能会写一些东西(http://robnapier.net/blog/equations-matrices-accelerate-607),但这可能需要一两个星期。 - Rob Napier

2
您没有展示路径是在哪里创建的。如果可能的话,您可以尝试在-drawLayer:inContext:方法中逐步构建路径,仅创建绘制所需的部分。
与所有性能问题一样,您应该使用Instruments来分析您的代码并找出瓶颈所在的确切位置。您已经尝试过了吗?如果是,您发现了什么?
顺便说一句,您使用CGPath而不是UIBezierPath有什么原因吗?从Apple's documentation中可以看到:
“对于在iOS中创建路径,建议您使用UIBezierPath而不是CGPath函数,除非您需要一些只有Core Graphics提供的功能,例如将椭圆添加到路径中。有关在UIKit中创建和渲染路径的更多信息,请参见“使用贝塞尔路径绘制形状”。”

+1 我之前没有看到过这个推荐使用 UIBezierPath 的注释。知道这点很有用。我想知道具体的原因是什么。 - Rob Napier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接