2009-12-30 7 views
10

Sto utilizzando uno NSView per ospitare diversi oggetti Core Animation CALayer. Quello che voglio essere in grado di fare è catturare un'istantanea dello stato attuale della vista come un'immagine bitmap.Come si esegue il rendering di una vista che contiene i livelli dell'anima core in una bitmap?

Questo è relativamente semplice, con una normale NSView utilizzando qualcosa di simile:

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 

Tuttavia, quando uso questo codice, gli strati Core Animation non sono resi.

Ho studiato CARenderer, poiché sembra che faccia ciò di cui ho bisogno, tuttavia non riesco a ottenere il rendering del mio albero di livelli esistente. Ho provato quanto segue:

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

Tuttavia, quando faccio questo ottengo un'eccezione:

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

Sto indovinando che questo deve essere perché l'albero strato è ospitato nel mio NSView e quindi attaccato al suo contesto. Non capisco come posso staccare l'albero dei livelli dallo NSView per renderlo in una bitmap, ed in questo caso non è banale creare un albero dei livelli duplicato.

C'è un altro modo per ottenere il CALayer s per il rendering in un bitmap? Non riesco a trovare alcun codice di esempio da nessuna parte per fare questo, in effetti non riesco a trovare alcun codice di esempio per CARenderer affatto.

risposta

15

C'è un bel post su "Cocoa è la mia ragazza" sulla registrazione di Core Animations. L'autore cattura l'intera animazione in un film, ma puoi usare la parte in cui afferra un singolo fotogramma.
Vai alla sezione "Come ottenere il frame corrente" in questo articolo:
http://www.cimgf.com/2009/02/03/record-your-core-animation-animation/

L'idea di base è:

  • Creare un CGContext
  • Usa CALayer'srenderInContext:
  • Creare un NSBitmapImageRep da il contesto (utilizzando CGBitmapContextCreateImage e NSBitmapImageRep's initWithCGImage)

Aggiornamento:
ho appena letto, che il metodo renderInContext: non supporta tutti i tipi di strati in Mac OS X 10.5. Non funziona per le seguenti classi di strati:

  • QCCompositionLayer
  • CAOpenGLLayer
  • QTMovieLayer
+0

Mille grazie, questo è esattamente ciò di cui avevo bisogno e funziona perfettamente. Vorrei aver notato il metodo '-renderInContext:' prima, 'CARenderer' mi ha guidato lungo il sentiero del giardino. –

+0

Questo lavoro per gli elementi è offscreen? Per esempio. il mio CALayer contiene alcuni elementi che non sono ancora nella cornice della vista, ma ho bisogno di scorrere per vederli. – Tudorizer

+0

Qualcuno ha ottenuto che funzioni con un'animazione iniziata con [UIView animateWithDuration: ...]? Non ha funzionato per me. – Isak

4

Se si desidera codice di esempio per come rendere una gerarchia CALayer a un NSImage (o UIImage per l'iPhone), puoi guardare il CPLayer del framework Core Plot e il suo -imageOfLayer method.Abbiamo effettivamente creato un percorso di rendering indipendente dal normale -renderInContext: processo utilizzato da CALayer, perché il modo normale non conserva gli elementi vettoriali durante la generazione di rappresentazioni PDF dei livelli. Ecco perché vedrai il metodo -recursivelyRenderInContext: in questo codice.

Tuttavia, questo non è di aiuto se si sta tentando di acquisire da uno qualsiasi dei tipi di livello menzionati da weichsel (QCCompositionLayer, CAOpenGLLayer o QTMovieLayer).

+0

Grazie per questo, è molto più completo della risposta di weichsel ma è più di quanto io abbia effettivamente bisogno in questo caso. Il codice '-recursivelyRenderInContext:' è davvero molto carino. –