Using Core Image Filters On/Under a NSWindow

So just to kick off my posts I thought I’d drop a little super-awesome Cocoa/CoreGraphics code to apply a Core Image filter to, or underneath, a window. This is similar to what Apple themselves use in Mac OS X v10.5 “Leopard” to create a blur under menus, etc.

Believe it or not, this is a private API on the desktop, so you ought to test and make sure it all works on your target OS before using it in a project!

Firstly, you’ll need to define some things:

typedef void * CGSConnection;
extern OSStatus CGSNewConnection(const void **attributes, CGSConnection * id);

Then you’ll need to use the following code to create a filter and apply it to a window. For this example, I use a blur placed beneath a window’s contents (similar to menus, or Glass in Vista). The code in awakeFromNib could be placed inside your App Delegate or somewhere similar. The filter is a standard Core Image CIFilter class as explained in the Core Image documentation. Not all filters work, but I leave that to you to experiment!

NB: For a window to accept a filter, the window needs to be at least 10% opaque (same as for receiving touch events). That means you have to make sure your window has a background colour and is not [NSColor clearColor].

-(void)awakeFromNib
{
NSWindow *w = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 256, 256) styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO];

[w setBackgroundColor:[NSColor colorWithDeviceWhite:1.0 alpha:0.2]];
[w setOpaque:NO];

[self enableBlurForWindow:w];

[w makeKeyAndOrderFront:nil];
}

-(void)enableBlurForWindow:(NSWindow *)window
{
CGSConnection thisConnection;
NSUInteger compositingFilter;

/*
Compositing Types

Under the window   = 1 <<  0
Over the window    = 1 <<  1
On the window      = 1 <<  2
*/

NSInteger compositingType = 1 << 0; // Under the window

/* Make a new connection to CoreGraphics */
CGSNewConnection(NULL, &thisConnection);

/* Create a CoreImage filter and set it up */
CGSNewCIFilterByName(thisConnection, (CFStringRef)@"CIGaussianBlur", &compositingFilter);
NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:3.0] forKey:@"inputRadius"];
CGSSetCIFilterValuesFromDictionary(thisConnection, compositingFilter, (CFDictionaryRef)options);

/* Now apply the filter to the window */
CGSAddWindowFilter(thisConnection, [window windowNumber], compositingFilter, compositingType);
}

9 Responses

05.05.09

Could someone upload a working version?

I swear, either I’m being incredibly thick (which is highly likely after a 4 hours Stats and Calc test) or somethings wrong with it

Confirmed working by another reader earlier; sorry!

:-)

05.05.09

Great tutorial! One thing that should probably be mentioned is: The NSWindow _must_ be visible when the filter is being applied to it, for this code to work.

05.05.09

So very cool! So cool I had to play around with it :)
Recorded a video: http://vimeo.com/4508431

05.05.09

Nevermind, found my problem :)

05.05.09

Nice tute, just wasted the last 20 minutes playing with other CI filters :D

05.05.09

Shouldn’t that be “an NSWindow”?

LOL. Sometimes, I start to wonder if I missed the memo that “an” has been deleted from the english language. :P

05.05.09

BTW, I forgot to mention: FREAKING AWESOME EFFECT!!!!

05.05.09

works just fine with clearColor background on the window as long as the window has some content in it

Leave Your Response

* Name, Email, Comment are Required

Related Posts