Skip navigation.
 
mlNSImage vs. CGLayer/CIImage
FROM : Manfred Schwind
DATE : Tue Nov 06 22:30:14 2007

I'm about to write an easy-to-use but fast little Offscreen class.
The interface for that class is very simply, something like that:

@interface Offscreen : NSObject {
}
- (id)initWithSize:(NSSize)size;
- (void)lockFocus;
- (void)unlockFocus;
- (void)drawInRect:(NSRect)rect operation:(NSCompositingOperation)
operation fraction:(float)fraction;
@end

So you can lock the focus into the offscreen to draw something into 
it and you can draw the offscreen somewhere.
Simple.

I wrote a little test that created such an Offscreen, draws something 
in it and then draws the offscreen 100 times in the current context 
(the whole test is done in the drawRect routine of an NSView).

Now I tried two different implementations:

1.) Just using an NSImage. Easy.

2.) Using CGLayerRef and CIImage.

The result is very interesting: NSImage is much faster than 
CGLayerRef and CIImage. Drawing the same image 100 times takes about 
0.25 Seconds when using CGLayerRef/CIImage, but when using NSImage it 
takes less than 0.01 Seconds. OK, drawing the first image in the 
CGLayerRef/CIImage method takes longer, but even if I begin the time 
measurement starting at the second draw call, it still takes 0.1 
Seconds. So NSImage is around 10 times faster than CGLayerRef/CIImage.
(Note: currently only tested on 10.4, time is measured with the 
Microseconds Carbon call to get wall time. Of course the CGLayerRef 
and the CIImage is just created _once_ and re-used for every drawing 
call - see also the code below.)

Then I tried to eliminate the use of the CIImage and directly draw 
the CGLayerRef with CGContextDrawLayerInRect. In this case it's 
approximately at the same speed as the NSImage solution. But I can 
not use the operation-Paramater so effectively it's always drawing in 
NSCompositeSourceOver mode. The NSImage method is very very fast in 
all operation modes.

Has anybody an idea why the use of CIImage to draw is so slow? How 
does NSImage do its magic? Are there other ways to draw CGLayerRef or 
CIImage that support the various compositing operations and alpha?

If you're interested, here are the relevant parts of the 
implementation (left out dealloc etc.):

1.) NSImage method (image is of kind NSImage):

- (void)lockFocus
{
   [image lockFocus];
}

- (void)unlockFocus
{
   [image unlockFocus];
}

- (void)drawInRect:(NSRect)rect operation:(NSCompositingOperation)
operation fraction:(float)fraction
{
   [image drawInRect:rect fromRect:NSMakeRect(0.0f, 0.0f, size.width, 
size.height) operation:operation fraction:fraction];
}

2.) GCLayerRef/CIImage method (image is of kind CIImage):

- (id)initWithSize:(NSSize)theSize
{
   if ((self = [super initWithSize:theSize]) != nil) {
       layer = CGLayerCreateWithContext([[NSGraphicsContext 
currentContext] graphicsPort], *(CGSize *)&size, NULL);
       layerContext = [[NSGraphicsContext 
graphicsContextWithGraphicsPort:CGLayerGetContext(layer) flipped:NO] 
retain];
   }
   return self;
}

- (void)lockFocus
{
   [image release];
   image = nil;
   oldContext = [NSGraphicsContext currentContext];
   [NSGraphicsContext setCurrentContext:layerContext];
}

- (void)unlockFocus
{
   [NSGraphicsContext setCurrentContext:oldContext];
}

- (void)drawInRect:(NSRect)rect operation:(NSCompositingOperation)
operation fraction:(float)fraction
{
   if (operation == NSCompositeSourceOver) {
       // fast
       CGContextDrawLayerInRect([[NSGraphicsContext currentContext] 
graphicsPort], *(CGRect *)&rect, layer);
   } else {
       // sloooooow!
       if (!image) {
           image = [[CIImage alloc] initWithCGLayer:layer];
       }
       [image drawInRect:rect fromRect:NSMakeRect(0.0f, 0.0f, size.width, 
size.height) operation:operation fraction:fraction];
   }
}

Thanks for any ideas/comments,
Mani
--
http://www.mani.de
iVolume - Loudness adjustment for iTunes.
LittleSecrets - The encrypted notepad.

Related mailsAuthorDate
mlNSImage vs. CGLayer/CIImage Manfred Schwind Nov 6, 22:30
mlRe: NSImage vs. CGLayer/CIImage Volker Runkel Nov 7, 13:52