Search code examples
cocoamacoscore-animation

Apply a CIFilter background filter when host window is transparent


I want to replicate the background of dock Stacks in grid and list mode. The background is translucent black with a blur effect:

Example of dock stack in grid mode http://www.thecustommac.com/wp-content/uploads/2009/09/stack-highlight.jpg

The problem is that [CALayer backgroundFilters] only applies to content in the window, the filters are not applied to content in other windows. Here's my code:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
{
    //make window transparent
    self.window.backgroundColor = [NSColor clearColor];
    [self.window setOpaque:NO];
    [self.window setHasShadow:NO];
    [self.window setStyleMask:NSBorderlessWindowMask];


    //make the content view layer hosting
    CALayer *rootLayer = [CALayer layer];
    [[self.window contentView] setLayer:rootLayer];
    [[self.window contentView] setWantsLayer:YES];


    //blur the background contents - NOT WORKING!
    [rootLayer setBackgroundColor:CGColorCreateGenericGray(0.0, .716)];

    CIFilter *blurFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
    [blurFilter setDefaults];
    [rootLayer setBackgroundFilters:[NSArray arrayWithObject: blurFilter]];
}

I can't think of how else to achieve this effect. (I've taken a look at the Display Services to see if there are any useful functions but I can't see any.)

Any ideas?


Solution

  • There is private API available this. Here is sample code by Rob Keniger:

    In 10.5 you can add any core image filter to a window using the private function 'CGSAddWindowFilter'.

    typedef void * CGSConnectionID;
    
    extern OSStatus CGSNewConnection(const void **attr, CGSConnectionID *id);
    
    - (void)enableBlurForWindow:(NSWindow *)window
    {
    
    CGSConnectionID _myConnection;
    uint32_t __compositingFilter;
    
    int __compositingType = 1; // Apply filter to contents underneath the window, then draw window normally on top
    
    /* Make a new connection to CoreGraphics, alternatively you could use the main connection*/
    
    CGSNewConnection(NULL , &_myConnection);
    
    /* The following creates a new CoreImage filter, then sets its options with a dictionary of values*/
    
    CGSNewCIFilterByName (_myConnection, (CFStringRef)@"CIGaussianBlur", &__compositingFilter);
    NSDictionary *optionsDict = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:3.0] forKey:@"inputRadius"];
    CGSSetCIFilterValuesFromDictionary(_myConnection, __compositingFilter, (CFDictionaryRef)optionsDict);
    
    /* Now just switch on the filter for the window */
    
    CGSAddWindowFilter(_myConnection, [window windowNumber], __compositingFilter, __compositingType );
    }