I have a CALayer that I apply a perspective to using a CGTransform3D
and specifying the m14
property. When the perspective is applied, the layer has jagged edges. I've heard people mention that adding a 1px transparent border around the layer will help with this. I don't know how to do that. I have tried using the border
and borderWidth
properties of a CALayer but the jagged edges are still there. I also tried to reduce the rect that is drawn by 1px on all sides, but it doesn't help either.
Any help would be great! Thanks!
By "I've heard people mention," I assume you mean the discussion on this question. What was suggested there was to actually draw the content in your CALayer so that it has a one-pixel transparent border outside of the core content, using the code
CGContextSetAllowsAntialiasing(theContext, true);
CGContextSetShouldAntialias(theContext, true);
within your Quartz drawing for that layer.
There's also the edgeAntialiasingMask
property on CALayer, but I've seen no impact when using code like the following:
layer.edgeAntialiasingMask = kCALayerLeftEdge | kCALayerRightEdge | kCALayerBottomEdge | kCALayerTopEdge;
on the antialiasing of transformed layer edges. See also this question for discussion of this, as well as how they solved their problem using one-pixel transparent borders around their images.
The best solution I found for this issue was to set shouldRasterize to YES and set the rasterization scale to the scale of the device's screen.
myLayer.shouldRasterize = YES;
myLayer.rasterizationScale = UIScreen.mainScreen().scale // iOS
myLayer.rasterizationScale = NSScreen.mainScreen()!.backingScaleFactor // OSX
That will in turn smooth out the edges for you.
Give your CALayer a mask that's inset 0.5 px from each edge:
UIView *aliasedView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, width, height)];
CALayer *maskLayer = [[CALayer alloc] init];
maskLayer.frame = CGRectMake(0.5, 0.5, width-1, height-1);
maskLayer.backgroundColor = [UIColor whiteColor].CGColor;
aliasedView.layer.mask = maskLayer;
A one pixel border smooths the edges of the content (!) of a layer:
- (UIImage *)drawAntiAliased:(UIImage *)image
{
const int B = 1; // Border width (anti-aliasing)
// Size of the output image
CGSize newImageSize = CGSizeMake(image.size.width + 2 * B, image.size.height + 2 * B);
UIGraphicsBeginImageContextWithOptions(newImageSize, NO, 0);
// Draw image with edge anti-aliasing
[image drawInRect:(CGRect){B, B, image.size}];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
If you want to use the border properties of the layer, this does no longer work, because the border is not part of the contents of the layer and is not affected by the smoothing.
Setting shouldRasterize
on the other hand affects both the content and the borders.
In Swift 3.0:
context.setAllowsAntialiasing(true)
context.setShouldAntialias(true)