2009-12-30 15 views
10

Estoy usando un NSView para alojar varios objetos de Core Animation CALayer. Lo que quiero poder hacer es tomar una instantánea del estado actual de la vista como una imagen de mapa de bits.¿Cómo renderizo una vista que contiene capas de animación principal en un mapa de bits?

Esto es relativamente simple, con una normal de NSView usar algo como esto:

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 

Sin embargo, cuando se utiliza este código, las capas de núcleo de animación no se representan.

He investigado CARenderer, ya que parece hacer lo que necesito, sin embargo, no puedo hacer que represente mi árbol de capas existente. He intentado lo siguiente:

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]]; 

Sin embargo, cuando hago esto me da una excepción:

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

supongo que esto debe ser porque el árbol de capas se encuentra alojado en mi NSView y por lo tanto, adjunto a su contexto. No entiendo cómo puedo separar el árbol de capas del NSView para convertirlo en un mapa de bits, y no es trivial en este caso crear un árbol de capas duplicadas.

¿Hay alguna otra forma de obtener el CALayer para renderizar en un mapa de bits? No puedo encontrar ningún código de muestra en ninguna parte para hacer esto, de hecho, no puedo encontrar ningún código de muestra para CARenderer.

Respuesta

15

Hay una gran publicación en "Cocoa is my girlfriend" sobre la grabación de Core Animations. El autor captura toda la animación en una película, pero puede usar la parte donde toma un solo cuadro.
Saltar a la sección "Obtención del marco actual" en este artículo:
http://www.cimgf.com/2009/02/03/record-your-core-animation-animation/

La idea básica es:

  • Crear un CGContext
  • Uso CALayer'srenderInContext:
  • Crear un NSBitmapImageRep de el contexto (usando CGBitmapContextCreateImage y initWithCGImage de NSBitmapImageRep)

Actualización:
que acabo de leer, que el método renderInContext: no es compatible con todo tipo de capas en Mac OS X 10.5. No funciona para las siguientes clases de capas:

  • QCCompositionLayer
  • CAOpenGLLayer
  • QTMovieLayer
+0

Muchas gracias, esto es exactamente lo que necesitaba y funciona perfectamente. Ojalá hubiera notado el método '-renderInContext:' anterior, 'CARenderer' me llevó por el camino del jardín. –

+0

¿Funciona esto para los elementos fuera de pantalla? P.ej. mi CALayer contiene algunos elementos que aún no están en el marco de la vista, pero necesito desplazarme para verlos. – Tudorizer

+0

¿Alguien ha conseguido que esto funcione con una animación iniciada con [UIView animateWithDuration: ...]? A mi no me sirvió. – Isak

4

Si desea código de ejemplo para el modo de hacer una jerarquía CALayer a un NSImage (o UIImage para el iPhone), puede ver el CPLayer del marco Core Plot y su -imageOfLayer method.De hecho, creamos una ruta de representación que es independiente del proceso normal -renderInContext: utilizado por CALayer, porque la forma normal no conserva los elementos vectoriales cuando se generan representaciones de capas en PDF. Es por eso que verá el método -recursivelyRenderInContext: en este código.

Sin embargo, esto no lo ayudará si está intentando capturar desde cualquiera de los tipos de capas mencionados por weichsel (QCCompositionLayer, CAOpenGLLayer o QTMovieLayer).

+0

Gracias por esto, es mucho más completo que la respuesta de weichsel pero es más de lo que realmente necesito en este caso. El código '-recursivelyRenderInContext:' es realmente muy agradable. –

Cuestiones relacionadas