NSImage vs. CGLayer/CIImage

  • 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.
  • Hi there,

    >
    > 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?
    >

    Is it possible that the NSImage caching is responsible for that? I
    usually have a way faster CIImage than NSImage.

    Volker
previous month november 2007 next month
MTWTFSS
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    
Go to today