CAShapeLayer as a mask for CALayer: rounded corners are stretched

I want to hide CALayer with CAShapeLayer , because changes to the form can be animated.

When I use CAShapeLayer as a mask, the rounded corners are stretched. However, if I take the same shape, create an NSImage with it and use the image to mask my CALayer , the rounded corners are fine.

Rounded corners with NSImage on the left, CAShapeLayer on the right

Here is the code I use to mask the layers (you can also download the entire sample project ):

 CGColorRef backgroundColor = CGColorCreateGenericRGB(0.0, 0.0, 0.0, 1.0f); [self.window.contentView setLayer:[CALayer layer]]; [self.window.contentView setWantsLayer:YES]; [[self.window.contentView layer] setFrame:[self.window.contentView frame]]; CALayer *imageBasedMaskLayer = [CALayer layer]; [imageBasedMaskLayer setContents:(id)[[self maskWithSize:NSMakeSize(50, 50)] CGImageForProposedRect:NULL context:nil hints:nil]]; [imageBasedMaskLayer setFrame:CGRectMake(0, 0, 50, 50)]; CALayer *layerWithImageBasedMaskLayer = [CALayer layer]; [layerWithImageBasedMaskLayer setBackgroundColor:backgroundColor]; [layerWithImageBasedMaskLayer setMask:imageBasedMaskLayer]; CAShapeLayer *shapeBasedMaskLayer = [CAShapeLayer layer]; CGPathRef maskShape = [self newMaskPathWithFrame:NSMakeRect(0, 0, 50, 50)]; [shapeBasedMaskLayer setPath:maskShape]; [shapeBasedMaskLayer setFillRule:kCAFillRuleEvenOdd]; CGPathRelease(maskShape); [shapeBasedMaskLayer setFrame:CGRectMake(0, 0, 50, 50)]; CALayer *layerWithShapeBasedMaskLayer = [CALayer layer]; [layerWithShapeBasedMaskLayer setBackgroundColor:backgroundColor]; [layerWithShapeBasedMaskLayer setMask:nil]; [layerWithShapeBasedMaskLayer setMask:shapeBasedMaskLayer]; [layerWithImageBasedMaskLayer setFrame:CGRectMake(50, 50, 50, 50)]; [layerWithShapeBasedMaskLayer setFrame:CGRectMake(120, 50, 50, 50)]; [[self.window.contentView layer] addSublayer:layerWithImageBasedMaskLayer]; [[self.window.contentView layer] addSublayer:layerWithShapeBasedMaskLayer]; CGColorRelease(backgroundColor); 

And the two methods that I use to create NSImage and CGPathRef .

 - (NSImage *)maskWithSize:(CGSize)size; { NSImage *maskImage = [[NSImage alloc] initWithSize:size]; [maskImage lockFocus]; [[NSColor blackColor] setFill]; CGPathRef mask = [self newMaskPathWithFrame:CGRectMake(0, 0, size.width, size.height)]; CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; CGContextAddPath(context, mask); CGContextFillPath(context); CGPathRelease(mask); [maskImage unlockFocus]; return [maskImage autorelease]; } - (CGPathRef)newMaskPathWithFrame:(CGRect)frame; { CGFloat cornerRadius = 3; CGFloat height = CGRectGetMaxY(frame); CGFloat width = CGRectGetMaxX(frame); CGMutablePathRef maskPath = CGPathCreateMutable(); CGPathMoveToPoint(maskPath, NULL, 0, height-cornerRadius); CGPathAddArcToPoint(maskPath, NULL, 0, height, cornerRadius, height, cornerRadius); CGPathAddLineToPoint(maskPath, NULL, width-cornerRadius, height); CGPathAddArcToPoint(maskPath, NULL, width, height, width, height-cornerRadius, cornerRadius); CGPathAddLineToPoint(maskPath, NULL, width, cornerRadius); CGPathAddArcToPoint(maskPath, NULL, width, 0, width-cornerRadius, 0, cornerRadius); CGPathAddLineToPoint(maskPath, NULL, cornerRadius, 0); CGPathAddArcToPoint(maskPath, NULL, 0, 0, 0, cornerRadius, cornerRadius); CGPathCloseSubpath(maskPath); return maskPath; } 

Note that the actual mask I want to create is harder than a rounded rectangle, otherwise I would use some of the simple drawing methods. I tried various CGPath drawing CGPath , the problem did not disappear.

What am I missing here?

+7
source share
1 answer

This is because CAShapeLayer supports speed over accuracy. That is why it can be sometimes.

The shape will be painted with smoothing, and if possible it will be displayed in the screen space before being rasterized to maintain resolution independence. However, some types of image processing operations, such as CoreImage filters applied to a layer or its ancestors, can cause rasterization in the local coordinate space.

Note: Form rasterization can help speed up accuracy. For example, pixels with multiple intersecting path segments may not give accurate results.

http://developer.apple.com/library/Mac/#documentation/GraphicsImaging/Reference/CAShapeLayer_class/Reference/Reference.html

Decision

you can use

 layerWithShapeBasedMaskLayer.cornerRadius = 3.0; 

round all your corners. You no longer need CAShapeLayer . You can even use CAShapeLayer as a sublevel to animate the path. Just install

 layerWithShapeBasedMaskLayer.masksToBounds = YES; 
+2
source

All Articles