如何缓存 CGContextRef

Posted

技术标签:

【中文标题】如何缓存 CGContextRef【英文标题】:How to cache CGContextRef 【发布时间】:2012-08-05 06:58:23 【问题描述】:

对我之前的结果不满意,我被要求创建一个在缩放时不会模糊的手绘视图。我能想到的唯一方法是使用CATiledLayer,否则在缩放时画线太慢了。目前,我已将其设置为每次都会重绘每一行,但我想知道我是否可以缓存前几行的结果(not 为像素,因为它们需要很好地缩放)在上下文或某事中。

我考虑过 CGBitmapContext,但这是否意味着我需要在每次缩放后拆除并设置一个新的上下文?问题是在视网膜显示器上,线条绘制太慢(在 iPad 2 上是马马虎虎),尤其是在缩放时绘制。 App Store 中有一个叫 GoodNotes 的应用程序,它精美地证明了这是可能的,并且可以顺利完成,但我无法理解他们是如何做到的。这是我到目前为止的代码(今天大部分时间的结果):

- (void)drawRect:(CGRect)rect
   
    CGContextRef c = UIGraphicsGetCurrentContext();

    CGContextSetLineWidth(c, mLineWidth);
    CGContextSetAllowsAntialiasing(c, true);
    CGContextSetShouldAntialias(c, true);
    CGContextSetLineCap(c, kCGLineCapRound);
    CGContextSetLineJoin(c, kCGLineJoinRound);

    //Protect the local variables against the multithreaded nature of CATiledLayer
    [mLock lock]; 
    NSArray *pathsCopy = [mStrokes copy];
    for(UIBezierPath *path in pathsCopy) //**Would like to cache these**
    
        CGContextAddPath(c, path.CGPath);
        CGContextStrokePath(c);
    
    if(mCurPath)
    
        CGContextAddPath(c, mCurPath.CGPath);
        CGContextStrokePath(c);
    

    CGRect pathBounds = mCurPath.bounds;
    if(pathBounds.size.width > 32 || pathBounds.size.height > 32)
    
        [mStrokes addObject:mCurPath];
        mCurPath = [[UIBezierPath alloc] init];
    
   [mLock unlock];

Profiling 显示目前最热门的函数是 GCSFillDRAM8by1

【问题讨论】:

【参考方案1】:

首先,由于路径描边是最昂贵的操作,您不应该锁定它,因为这会阻止您在不同的核心上同时绘制图块。

其次,我认为您可以通过在上下文中添加所有路径并将它们完全抚摸来避免多次调用CGContextStrokePath

[mLock lock]; 
for ( UIBezierPath *path in mStrokes ) 
    CGContextAddPath(c, path.CGPath);

if ( mCurPath ) 
    CGContextAddPath(c, mCurPath.CGPath);

CGRect pathBounds = mCurPath.bounds;
if ( pathBounds.size.width > 32 || pathBounds.size.height > 32 )

    [mStrokes addObject:mCurPath];
    mCurPath = [[UIBezierPath alloc] init];

[mLock unlock];
CGContextStrokePath(c);

CGContextRef 只是在其中进行绘图操作的画布。您无法缓存它,但您可以使用路径的扁平位图图像创建CGImageRef 并重用该图像。这对缩放没有帮助(因为您需要在细节级别发生变化时重新创建图像),但对于提高用户绘制非常长的路径时的性能很有用。

关于这个主题有一个非常有趣的 WWDC 2012 会议视频:Optimizing 2D Graphics and Animation Performance。

【讨论】:

我实际上已经看到了那个会话,但不幸的是它没有考虑到扩展。实际上我相信我已经找到了我的瓶颈(根本不是 drawRect 的一部分)。在进行更多测试后,我将发布更多信息。 我发现了瓶颈,但你的建议也很有帮助,而且很准确,所以我给了你一个 +1【参考方案2】:

瓶颈实际上是我使用 CATiledLayer 的方式。我想用手绘信息更新太多了。正如我在在线文档和教程中看到的那样,我设置了详细程度,但最终我并不需要那么多。我刚刚连接了滚动视图委托,在完成缩放时清除了内容并更改了图层的contentScale 以匹配滚动视图。结果很漂亮(它会消失并重新消失,但这无济于事)。

【讨论】:

以上是关于如何缓存 CGContextRef的主要内容,如果未能解决你的问题,请参考以下文章

如何让CDN 缓存动态页面

探讨下如何更好的使用缓存 —— Redis缓存的特殊用法以及与本地缓存一起构建多级缓存的实现

如何清理缓存数据

如何使用yii2的缓存依赖特性

spring应用启动时如何制作缓存依赖于其他缓存

apache缓存如何清理