views:

34

answers:

1

I'd like to multiply r, g, b and a values of two CGImages, to use a single image as both a mask (in its alpha channel) and tint (in the rgb channels). I first tried simply:

CGContextDrawImage(context, CGRectMake(0, 0, _w, _h), handle());
CGContextSetBlendMode(context, kCGBlendModeMultiply);
CGContextDrawImage(context, CGRectMake(0, 0, other->width(), other->height()), over);

However, this only tints, and does not mask. Thus, I extracted the alpha from the masking image into a CGImage mask grayscale image (using a CGDataProvider) and used that as a mask on the context:

// "mask" is "other"'s alpha channel as an inverted grayscale image
CGContextSaveGState(context);
CGContextClipToMask(context, CGRectMake(0, 0, other->width(), other->height()), mask);
CGImageRelease(mask);
CGContextDrawImage(context, CGRectMake(0, 0, _w, _h), handle());
CGContextRestoreGState(context); // Don't apply the clip mask to 'other', since it already has it
CGContextSetBlendMode(context, kCGBlendModeMultiply);
CGContextDrawImage(context, CGRectMake(0, 0, other->width(), other->height()), other->handle());

It still won't look right, though. The image gets lighter; should a multiplied image be always at least darker? Previews of images at:

http://dl.dropbox.com/u/6775/multiply/index.html

Thanks in advance for enlightenment :)

+1  A: 

The first thing is that you don't need to extract the alpha channel to a separate channel to do masking. You can just draw it with kCGBlendModeDestinationIn. For an inverted mask, same thing, but with kCGBlendModeDestinationOut.

So, the solution is to draw the second image with Multiply, then draw it again with Destination In or Out.

Peter Hosey
That is an *excellent* solution, but I just realized why I never considered it: this app must support 10.4 :( (I'll mark it as correct though, as it *is* correct for the stated question)
Joachim Bengtsson