2006/02/27
Quartz Musings (V): GPU Filters
What are the main concepts in Core Image ?
- A CIImage is the image abstraction. It can be an outside source, a previous step.
- The CIContext is the context abstraction. What is interesting is that you can create it from an OpenGL context or a CoreGraphics context (of any kind! imagine onscreen rendering through a regular CGContext or offscreen computation in a bitmap context for a command-line tool).
- The CIFilter describes a computation to be applied to an image (or multiple images, for instance if you want to display a transition or blend images), with customizable (and discoverables) parameters, and produces a result CIImage. You can write your own (in a subset of the OpenGL Shading language) or use one the 60 filters shipping in OS X.
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 !
