【问题标题】:How do I render a view which contains Core Animation layers to a bitmap?如何将包含核心动画层的视图渲染到位图?
【发布时间】:2010-12-31 00:03:55
【问题描述】:

我正在使用 NSView 来托管多个 Core Animation CALayer 对象。我想要做的是获取视图当前状态的快照作为位图图像。

这对于普通的NSView 使用这样的东西相对简单:

void ClearBitmapImageRep(NSBitmapImageRep* bitmap) {
    unsigned char* bitmapData = [bitmap bitmapData];
    if (bitmapData != NULL)
        bzero(bitmapData, [bitmap bytesPerRow] * [bitmap pixelsHigh]);
}

@implementation NSView (Additions)
- (NSBitmapImageRep*)bitmapImageRepInRect:(NSRect)rect
{
    NSBitmapImageRep* imageRep = [self bitmapImageRepForCachingDisplayInRect:rect];
    ClearBitmapImageRep(imageRep);
    [self cacheDisplayInRect:rect toBitmapImageRep:imageRep];
    return imageRep;
}
@end

但是,当我使用这段代码时,Core Animation 层没有被渲染。

我已经调查了CARenderer,因为它似乎可以满足我的需要,但是我无法让它渲染我现有的图层树。我尝试了以下方法:

NSOpenGLPixelFormatAttribute att[] = 
{
    NSOpenGLPFAWindow,
    NSOpenGLPFADoubleBuffer,
    NSOpenGLPFAColorSize, 24,
    NSOpenGLPFAAlphaSize, 8,
    NSOpenGLPFADepthSize, 24,
    NSOpenGLPFANoRecovery,
    NSOpenGLPFAAccelerated,
    0
};

NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:att];
NSOpenGLView* openGLView = [[NSOpenGLView alloc] initWithFrame:[self frame] pixelFormat:pixelFormat];
NSOpenGLContext* oglctx = [openGLView openGLContext];

CARenderer* renderer = [CARenderer rendererWithCGLContext:[oglctx CGLContextObj] options:nil];
renderer.layer = myContentLayer;
[renderer render];
NSBitmapImageRep* bitmap = [oglView bitmapImageRepInRect:[oglView bounds]];

但是,当我这样做时,我得到了一个例外:

CAContextInvalidLayer -- layer <CALayer: 0x1092ea0> is already attached to a context

我猜这一定是因为层树托管在我的NSView 中,因此附加到它的上下文中。我不明白如何将层树与NSView 分离以将其渲染为位图,在这种情况下创建重复的层树并非易事。

还有其他方法可以让CALayers 渲染到位图吗?我在任何地方都找不到任何示例代码来执行此操作,事实上我根本找不到CARenderer 的任何示例代码。

【问题讨论】:

    标签: objective-c cocoa core-animation bitmap rendering


    【解决方案1】:

    关于录制核心动画的“可可是我的女朋友”上有一篇很棒的帖子。作者将整个动画捕捉到电影中,但您可以使用他抓取单个帧的部分。
    跳转到本文“获取当前帧”部分:
    http://www.cimgf.com/2009/02/03/record-your-core-animation-animation/

    基本思路是:

    • 创建 CGContext
    • 使用CALayer'srenderInContext:
    • 从 上下文(使用 CGBitmapContextCreateImage 和 NSBitmapImageRep 的 initWithCGImage)

    更新:
    我刚刚读到,renderInContext: 方法不支持 Mac OS X 10.5 中的所有类型的层。 它不适用于以下图层类:

    • QCCompositionLayer
    • CAOpenGLLayer
    • QTMovieLayer

    【讨论】:

    • 非常感谢,这正是我所需要的,而且效果很好。我希望我早点注意到-renderInContext: 方法,CARenderer 引导我走上花园小径。
    • 这对屏幕外的元素有用吗?例如。我的 CALayer 包含一些尚未出现在视图框架中的元素,但我需要滚动才能看到它们。
    • 有没有人用 [UIView animateWithDuration:...] 开始的动画来处理这个?它对我不起作用。
    【解决方案2】:

    如果您想了解如何将 CALayer 层次结构渲染到 NSImage(或 iPhone 的 UIImage)的示例代码,您可以查看 Core Plot 框架的 CPLayer 及其 -imageOfLayer method。我们实际上创建了一个独立于 CALayer 使用的正常 -renderInContext: 进程的渲染路径,因为正常方式在生成图层的 PDF 表示时不会保留矢量元素。这就是为什么您会在此代码中看到 -recursivelyRenderInContext: 方法。

    但是,如果您尝试从 weichsel 提到的任何图层类型(QCCompositionLayer、CAOpenGLLayer 或 QTMovieLayer)进行捕捉,这对您没有帮助。

    【讨论】:

    • 谢谢你,它比 weichsel 的答案更全面,但在这种情况下它比我实际需要的要多。 -recursivelyRenderInContext: 代码确实非常好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多