views:

782

answers:

2

I have a grayscale image which I want to use for drawing Cocoa controls. The image has various levels of gray. Where it is darkest, I want it to draw a specified tint color darkest. I want it to be transparent where the source image is white.

Basically, I want to reproduce the behavior of tintColor seen in UINavigationBar on the iPhone.

So far, I have explored several options:

  • Draw the tint color over the grayscale image using SourceOver composition -> This requires a non-opaque tint color -> The result comes out much darker than desired

  • Use a CIMultiplyCompositing CIFilter to tint the image -> I can't [CIImage drawAtPoint:fromRect:operation:fraction:] to draw only part of the image. The same works fine with NSImage -> I get occasional crashes which I cannot make sense of

  • Transform the grayscale image into a mask. I.e. Black should be opaque. White should be transparent. Gray should have intermediate alpha values. -> This would seem to be the best solution -> Try as I might, I cannot achieve this.

+1  A: 

The CIMultiplyCompositing filter is definitely the way to do this. If it's crashing, can you post a stack trace? I use CIFilters heavily and don't have crashing issues.

//assume inputImage is the greyscale CIImage you want to tint

CIImage* outputImage = nil;

//create some green
CIFilter* greenGenerator = [CIFilter filterWithName:@"CIConstantColorGenerator"];
CIColor* green = [CIColor colorWithRed:0.30 green:0.596 blue:0.172];
[greenGenerator setValue:green forKey:@"inputColor"];
CIImage* greenImage = [greenGenerator valueForKey:@"outputImage"];

//apply a multiply filter
CIFilter* filter = [CIFilter filterWithName:@"CIMultiplyCompositing"];
[filter setValue:greenImage forKey:@"inputImage"];
[filter setValue:inputImage forKey:@"inputBackgroundImage"];
outputImage = [filter valueForKey:@"outputImage"];

[outputImage drawAtPoint:NSZeroPoint fromRect:NSRectFromCGRect([outputImage extent]) operation:NSCompositeCopy fraction:1.0];
Rob Keniger
A: 
- (NSImage *)imageTintedWithColor:(NSColor *)tint 
{
    if (tint != nil) {
     NSSize size = [self size];
     NSRect bounds = { NSZeroPoint, size };
     NSImage *tintedImage = [[NSImage alloc] initWithSize:size];

     [tintedImage lockFocus];

     CIFilter *colorGenerator = [CIFilter filterWithName:@"CIConstantColorGenerator"];
     CIColor *color = [[[CIColor alloc] initWithColor:tint] autorelease];

     [colorGenerator setValue:color forKey:@"inputColor"];

     CIFilter *monochromeFilter = [CIFilter filterWithName:@"CIColorMonochrome"];
     CIImage *baseImage = [CIImage imageWithData:[self TIFFRepresentation]];

     [monochromeFilter setValue:baseImage forKey:@"inputImage"];  
     [monochromeFilter setValue:[CIColor colorWithRed:0.75 green:0.75 blue:0.75] forKey:@"inputColor"];
     [monochromeFilter setValue:[NSNumber numberWithFloat:1.0] forKey:@"inputIntensity"];

     CIFilter *compositingFilter = [CIFilter filterWithName:@"CIMultiplyCompositing"];

     [compositingFilter setValue:[colorGenerator valueForKey:@"outputImage"] forKey:@"inputImage"];
     [compositingFilter setValue:[monochromeFilter valueForKey:@"outputImage"] forKey:@"inputBackgroundImage"];

     CIImage *outputImage = [compositingFilter valueForKey:@"outputImage"];

     [outputImage drawAtPoint:NSZeroPoint
         fromRect:bounds
           operation:NSCompositeCopy
         fraction:1.0];

     [tintedImage unlockFocus];  

     return [tintedImage autorelease];
    }
    else {
     return [[self copy] autorelease];
    }
}

- (NSImage*)imageCroppedToRect:(NSRect)rect
{
    NSPoint point = { -rect.origin.x, -rect.origin.y };
    NSImage *croppedImage = [[NSImage alloc] initWithSize:rect.size];

    [croppedImage lockFocus];
    {
     [self compositeToPoint:point operation:NSCompositeCopy];
    }
    [croppedImage unlockFocus];

    return [croppedImage autorelease];
}
Pierre Bernard