2006/02/27

Quartz Musings (V): GPU Filters

Core Image is an interesting image processing framework in Mac OS X 10.4. The basic principles are very simple: setup a pipeline of effects, and render it. But the interesting part is that Core Image tries to leverage the GPU as much as possible, by computing the filters on the GPU, which means that Core Image uses the GPU and OpenGL as a generic 'pixel computing' engine (or fall back on the CPU if needed).

What are the main concepts in Core Image ?

Let's add another way of rendering our game of life grid. We will add a realtime zoom blur effect on top.

The first thing is to create the CIContext and our filter pipeline, which will only contain the zoom blur filter. We could create it from an OpenGL context and reuse our NSOpenGLView subclass, but for simplicity sake, let's just take the plain CoreGraphics route.

We already saw how to get a CoreGraphics context: if we're in a Cocoa drawRect: method just obtain it through the currentContext. Then, the Core Image context can be create by passing this regular CGContext.

 NSGraphicsContext* nsctx = [NSGraphicsContext currentContext];
 CGContextRef context = [nsctx graphicsPort];
  
 _cicontext = [[CIContext contextWithCGContext:context options:nil] retain];
Next, we'd like to use our still unchanged rendering code, and feed the result to CoreImage. Conveniently, CIContext provides a way to create a CGLayer from a CIContext, and a CGLayer has an associated CoreGraphics context, so we'll be able to plug our rendering code here.

We will also create our Core Image filter. Filters can be discovered by exploring broad categories (color management, distortion, compositing, tiling, ...) or by name, which is what we do here. Another intersting choice with Core Image filters is how parameters are handled with generic key-value expression. It makes extremely easy to plug a filter paramater to a GUI binding, and setting a parameter is clearly not something in the performance hot spot.

The zoom blur filter offers two (or three, counting the input image) parameters: the zoom center and zoom/blur amount.

 CGSize =  CGSizeMake([self bounds].size.width,[self bounds].size.height);

 _cicontext = [[CIContext contextWithCGContext:context options:nil] retain];
 _layer   = [_cicontext createCGLayerWithSize: CGSizeMake(size.width, 
                       size.height)  info: nil];
 _cgcontext = CGLayerGetContext(_layer);  

 _filter = [[CIFilter filterWithName:@"CIZoomBlur"] retain]; 
 [_filter setDefaults]; 
 [_filter setValue:[CIVector vectorWithX:(size.width/2) Y:(size.height/2)] 
            forKey:@"inputCenter"];
 _filter setValue:[NSNumber numberWithFloat:5] 
           forKey:@"inputAmount"];
The actual rendering will be a three-step process: extract a CIImage from the CGLayer (easy, there is a constructor for that), feed the image to our filter (we could also change a filter parameter here for instance to have an increasing/decreasing zoom effect in real time), get the resulting _CIImage image

 CIImage* input;
 CIImage* result;

 // Actual rendering code, applied to _cgcontext
 
 input = [[CIImage alloc] initWithCGLayer:_layer];
 [_filter setValue: input forKey: @"inputImage"]; 
 result = [_filter valueForKey:@"outputImage"];
 
 [_cicontext drawImage:result atPoint:CGPointZero fromRect:bounds];
 [input release];
Tadaam !


Comments: Post a Comment

<< Home

This page is powered by Blogger. Isn't yours?