0

This is best I've come up with for blitting a 24-bit BGR image out to an NSView.

I did trim a significant amount of CPU time by ensuring that the NSWindow host also had the same colorSpace.

I think there are 4 or 5 pixel copies going on here:

  1. in the vImage conversion (required)
  2. calling CGDataProviderCreateWithData
  3. calling CGImageCreate
  4. creating the NSBitmapImageRep bitmap
  5. in the final blit with drawInRect (required)

Anyone want to chime in on improving it?

Any help would be much appreciated.

{
   // one-time setup code
   CGColorSpaceRef useColorSpace = nil; 
   int w = 1920;
   int h = 1080;
   [theWindow setColorSpace: [NSColorSpace genericRGBColorSpace]];
   // setup vImage buffers (not listed here)
   // srcBuffer is my 24-bit BGR image (malloc-ed to be w*h*3)
   // dstBuffer is for the resulting 32-bit RGBA image (malloc-ed to be w*h*4)

   ...
   // this is called @ 30-60fps
   if (!useColorSpace)
      useColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
   vImage_Error err = vImageConvert_BGR888toRGBA8888(srcBuffer, NULL, 0xff, dstBuffer, NO, 0);

   CGDataProviderRef newProvider = CGDataProviderCreateWithData(NULL,dstBuffer->data,w*h*4,myReleaseProvider);
   CGImageRef myImageRGBA = CGImageCreate(w, h, 8, 32, w*4, useColorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaLast, newProvider, NULL, false, kCGRenderingIntentDefault);
   CGDataProviderRelease(newProvider);
   // store myImageRGBA in an array of frames (using NSObject wrappers) for later access (setNeedsDisplay:)
   ... 
}

- (void)drawRect:(NSRect)dirtyRect
{
   // this is called @ 30-60fps
   CGImageRef storedImage = ...;  // retrieve from array
   NSBitmapImageRep *repImg = [[NSBitmapImageRep alloc] initWithCGImage:storedImage];
   CGRect myFrame = CGRectMake(0,0,CGImageGetWidth(storedImage),CGImageGetHeight(storedImage));
   [repImg drawInRect:myFrame fromRect:myFrame operation:NSCompositeCopy fraction:1.0 respectFlipped:TRUE hints:nil];

   // free image from array (not listed here)
}

// this is called when the CGDataProvider is ready to release its data
void myReleaseProvider (void *info, const void *data, size_t size)
{
    if (data)  {
        free((void *)data);
        data=nil;
    }
}
Stephen Canon
  • 103,815
  • 19
  • 183
  • 269
zzyzy
  • 973
  • 6
  • 21

1 Answers1

2

Use CGColorSpaceCreateDeviceRGB instead of genericRGB to avoid colorspace conversion inside CG. Use kCGImageAlphaNoneSkipLast instead of kCGImageAlphaLast since we know alpha is opaque to allow for a copy instead of a blend.

After you make those changes, it would be useful to run an Instruments time profile on it to show where the time is going.

Ian Ollmann
  • 1,592
  • 9
  • 16