Reputation: 39681
I need a UIImageView that can draw itself in color or b/w according to a flag:
BOOL isGrey;
I'm trying to do it by drawing a black rectangle on top of the original image with the Quartz blendmode set to Color. This works except it doesn't respect the image's alpha mask.
See illustration: alt text http://img214.imageshack.us/img214/1407/converttogreyscaleillo.png
Searching Google and SO, I found and tried several solutions but none respect the mask either.
Here is the code that produces the 'What I get' image above:
- (void)drawRect:(CGRect)rect {
if (isGrey) {
CGContextRef context = UIGraphicsGetCurrentContext();
// flip orientation
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// draw the image
CGContextDrawImage(context, self.bounds, self.image.CGImage);
// set the blend mode and draw rectangle on top of image
CGContextSetBlendMode(context, kCGBlendModeSaturation);
CGContextSetRGBFillColor(context, 0.0, 0.0, 0.0, 1.0);
CGContextFillRect(context, rect);
} else {
[self.image drawInRect:rect];
}
}
Is there some Quartz drawing mode that I'm forgetting to set? I've looked thru the Quartz Programming Guide but is so hard to extract the one bit of info you need from the overlapping and hyperlinked subjects.
Obviously I'm looking for a general solution that will apply to images with any masked shape, not just the circle shown.
Upvotes: 9
Views: 18204
Reputation: 39681
To draw a shape while respecting an image's alpha mask, just add one line before you draw:
CGContextClipToMask(context, self.bounds, image.CGImage);
// example usage
- (void)drawRect:(CGRect)rect {
if (isGrey) {
CGContextRef context = UIGraphicsGetCurrentContext();
// flip orientation
CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// draw the image
CGContextDrawImage(context, self.bounds, self.image.CGImage);
// set the blend mode and draw rectangle on top of image
CGContextSetBlendMode(context, kCGBlendModeColor);
CGContextClipToMask(context, self.bounds, image.CGImage); // respect alpha mask
CGContextSetRGBFillColor(context, 0.0, 0.0, 0.0, 1.0);
CGContextFillRect(context, rect);
} else {
[self.image drawInRect:rect];
}
}
Upvotes: 11
Reputation: 39376
I'd take the easy way out and draw a black circle with the same size as the round image you have. Probably not what you are asking for.
Upvotes: 1
Reputation: 32336
Although it doesn't directly answer your question, you may be able to get the effect you are looking for by using the kCGBlendModeLuminosity
blend mode:
- (void)drawRect:(CGRect)rect {
CGSize imageSize = [image size];
CGContextRef c = UIGraphicsGetCurrentContext();
if (isGrey)
CGContextSetBlendMode(c, kCGBlendModeLuminosity);
CGContextScaleCTM(c, 1.0, -1.0);
CGContextDrawImage(c, CGRectMake(0.0f, 0.0f, imageSize.width, -imageSize.height), [image CGImage]);
}
Also, the rect passed to drawRect:
is the invalid area, not the entire area. You should be using the bounds
instead
Upvotes: 1
Reputation: 24476
You could try to add a mask to the view's Core Animation layer.
CALayer *maskLayer = [CALayer layer];
[maskLayer setBounds:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)];
// Center the layer
[maskLayer setPosition:CGPointMake([self view].bounds.size.width/2,
[self view].bounds.size.height/2)];
// Set the cornerRadius to half the width/height to get a circle.
[maskLayer setCornerRadius:50.f];
[maskLayer setMasksToBounds:YES];
// Any solid color will do. Just using black here.
[maskLayer setBackgroundColor:[[UIColor blackColor] CGColor]];
// Set the mask which will only allow that which is in the
// circle show through
[[[self view] layer] setMask:maskLayer];
One other thought. Why can't you just create a b&w version of the image and swap that out depending on your flag?
Upvotes: 0