在多个CGPath的内部边缘绘制光晕

图片http://img403.imageshack.us/img403/9582/paths.jpg

如果通过将左图像所示的两个圆形path相加来创buildCGMutablePathRef,是否可以获得最终的CGPathRef,该图像仅表示右边图像所示的外边框?

谢谢你的帮助!

你要求的是贝塞尔path的联合。 苹果公司不提供任何API来计算path的联合。 这实际上是一个相当复杂的algorithm。 这里有几个链接:

如果你解释一下你想要在工会path上做什么,我们可能会提出一些不需要实际计算工会的替代scheme。

你可以画出一个相当不错的内在辉光,而不用实际计算path的联合。 相反,做一个位图。 填充位图上的每个path。 你会用这个作为面具。 接下来,创build一个面具的反转图像,其中包含的所有区域都被填充。 你会画这个让CoreGraphics在联合的内部边缘上画一个阴影。 最后,将掩码设置为您的CGContext掩码,设置阴影参数并绘制反转图像。

好的,这听起来很复杂。 但是看起来是这样的(视网膜版本在右边):

辉光发光视网膜

这并不完美(angular落太轻),但它是相当不错的。

所以这是代码。 我传递UIBezierPaths而不是CGPaths,但它们之间的转换是微不足道的。 我使用了一些UIKit函数和对象。 请记住,您始终可以使用UIGraphicsPushContextUIGraphicsPopContext将UIKit绘制到任意的CGContext。

首先,我们需要一个蒙版图像。 它应该是一个只有alpha通道的图像,在任何path中都是1,而在所有path之外都是0。 这个方法返回这样一个图像:

 - (UIImage *)maskWithPaths:(NSArray *)paths bounds:(CGRect)bounds { // Get the scale for good results on Retina screens. CGFloat scale = [UIScreen mainScreen].scale; CGSize scaledSize = CGSizeMake(bounds.size.width * scale, bounds.size.height * scale); // Create the bitmap with just an alpha channel. // When created, it has value 0 at every pixel. CGContextRef gc = CGBitmapContextCreate(NULL, scaledSize.width, scaledSize.height, 8, scaledSize.width, NULL, kCGImageAlphaOnly); // Adjust the current transform matrix for the screen scale. CGContextScaleCTM(gc, scale, scale); // Adjust the CTM in case the bounds origin isn't zero. CGContextTranslateCTM(gc, -bounds.origin.x, -bounds.origin.y); // whiteColor has all components 1, including alpha. CGContextSetFillColorWithColor(gc, [UIColor whiteColor].CGColor); // Fill each path into the mask. for (UIBezierPath *path in paths) { CGContextBeginPath(gc); CGContextAddPath(gc, path.CGPath); CGContextFillPath(gc); } // Turn the bitmap context into a UIImage. CGImageRef cgImage = CGBitmapContextCreateImage(gc); CGContextRelease(gc); UIImage *image = [UIImage imageWithCGImage:cgImage scale:scale orientation:UIImageOrientationDownMirrored]; CGImageRelease(cgImage); return image; } 

那实际上是困难的部分。 现在我们需要在掩模(path联合)区域以外的任何地方都有我们的发光颜色的图像。 我们可以使用UIKit函数使其比纯粹的CoreGraphics方法更容易:

 - (UIImage *)invertedImageWithMask:(UIImage *)mask color:(UIColor *)color { CGRect rect = { CGPointZero, mask.size }; UIGraphicsBeginImageContextWithOptions(rect.size, NO, mask.scale); { // Fill the entire image with color. [color setFill]; UIRectFill(rect); // Now erase the masked part. CGContextClipToMask(UIGraphicsGetCurrentContext(), rect, mask.CGImage); CGContextClearRect(UIGraphicsGetCurrentContext(), rect); } UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; } 

使用这两个图像,我们可以为当前的UIKitgraphics上下文绘制一个内部发光的path数组:

 - (void)drawInnerGlowWithPaths:(NSArray *)paths bounds:(CGRect)bounds color:(UIColor *)color offset:(CGSize)offset blur:(CGFloat)blur { UIImage *mask = [self maskWithPaths:paths bounds:bounds]; UIImage *invertedImage = [self invertedImageWithMask:mask color:color]; CGContextRef gc = UIGraphicsGetCurrentContext(); // Save the graphics state so I can restore the clip and // shadow attributes after drawing. CGContextSaveGState(gc); { CGContextClipToMask(gc, bounds, mask.CGImage); CGContextSetShadowWithColor(gc, offset, blur, color.CGColor); [invertedImage drawInRect:bounds]; } CGContextRestoreGState(gc); } 

为了testing它,我使用几个圆圈创build了一个图像,并将其放在UIImageView中:

 - (void)viewDidLoad { [super viewDidLoad]; UIBezierPath *path1 = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(20, 20, 60, 60)]; UIBezierPath *path2 = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 50, 60, 60)]; NSArray *paths = [NSArray arrayWithObjects:path1, path2, nil]; UIGraphicsBeginImageContextWithOptions(self.imageView.bounds.size, NO, 0.0); { [self drawInnerGlowWithPaths:paths bounds:self.imageView.bounds color:[UIColor colorWithHue:0 saturation:1 brightness:.8 alpha:.8] offset:CGSizeZero blur:10.0]; } imageView.image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); } 

Swift 3中有两种方法:

样品

path代码:

  let radius = rect.height * 0.25 let centerX = rect.width * 0.5 let centerY = rect.height * 0.5 let arcCenterOffset = radius - radius * 0.5 * sqrt(3) let degree:(_: CGFloat) -> CGFloat = { return CGFloat.pi * $0 / 180 } let gourd = UIBezierPath() let circle1 = UIBezierPath(arcCenter: CGPoint(x: centerX - radius + arcCenterOffset, y: centerY), radius: radius, startAngle: degree(-30), endAngle: degree(30), clockwise: false) let circle2 = UIBezierPath(arcCenter: CGPoint(x: centerX + radius - arcCenterOffset, y: centerY ), radius: radius, startAngle: degree(150), endAngle: degree(-150), clockwise: false) gourd.append(circle1) gourd.append(circle2) let gourdInverse = UIBezierPath(cgPath: gourd.cgPath) let infiniteRect = UIBezierPath(rect: .infinite) gourdInverse.append(infiniteRect) guard let c = UIGraphicsGetCurrentContext() else { fatalError("current context not found.") } 
  1. 甚至奇数的填充规则:

     c.beginPath() c.addPath(gourdInverse.cgPath) c.setShadow(offset: CGSize.zero, blur: 10, color: UIColor.red.cgColor) c.setFillColor(UIColor(white: 1, alpha: 1).cgColor) c.fillPath(using: .evenOdd) 
  2.  c.beginPath() c.addPath(gourd.cgPath) c.clip() c.beginPath() c.addPath(gourdInverse.cgPath) c.setShadow(offset: CGSize.zero, blur: 10, color: UIColor.red.cgColor) c.fillPath()