Iphone 围绕多条路径的内边缘绘制辉光

Iphone 围绕多条路径的内边缘绘制辉光,iphone,ios,quartz-2d,cgpath,Iphone,Ios,Quartz 2d,Cgpath,如果我通过将左图所示的两个循环路径相加来创建一个CGMutablePathRef,是否可能获得仅表示右图所示外部边界的最终CGPathRef 谢谢你的帮助 您需要的是贝塞尔路径的并集。苹果没有提供任何用于计算路径并集的API。这实际上是一个相当复杂的算法。以下是几个链接: 如果你解释一下你想用联合路径做什么,我们可能会建议一些不需要实际计算联合的替代方案 您可以绘制一个相当不错的内部辉光,而无需实际计算路径的并集。相反,制作一个位图。填充位图上的每个路径。你可以用这个做面具。接下来,创建

如果我通过将左图所示的两个循环路径相加来创建一个CGMutablePathRef,是否可能获得仅表示右图所示外部边界的最终CGPathRef


谢谢你的帮助

您需要的是贝塞尔路径的并集。苹果没有提供任何用于计算路径并集的API。这实际上是一个相当复杂的算法。以下是几个链接:

如果你解释一下你想用联合路径做什么,我们可能会建议一些不需要实际计算联合的替代方案

您可以绘制一个相当不错的内部辉光,而无需实际计算路径的并集。相反,制作一个位图。填充位图上的每个路径。你可以用这个做面具。接下来,创建蒙版的反转图像,该图像填充联合区域之外的所有内容。您将绘制此图,以使CoreGraphics围绕联合体的内边缘绘制阴影。最后,将遮罩设置为CGContext遮罩,设置阴影参数,并绘制反转图像

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

它不是完美的(拐角处太轻),但它相当不错

这是代码。我传递的是uibezierpath而不是cgpath,但是在它们之间进行转换很简单。我使用了一些UIKit函数和对象。请记住,始终可以使用
UIGraphicsPushContext
UIGraphicsPopContext
将UIKit绘制到任意CGContext

首先,我们需要一个遮罩图像。它应该是仅alpha通道的图像,在任何路径内为1,在所有路径外为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;
}
这其实是最难的部分。现在,我们需要一个图像,它是遮罩(路径联合)区域外的所有发光颜色。我们可以使用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;
}
通过这两幅图像,我们可以在当前UIKit图形上下文中绘制一个内部光晕,以显示一系列路径:

- (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);
}
为了测试它,我使用几个圆圈创建了一个图像,并将其放在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中有两种方式:

路径代码:

    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.")
    }
  • 奇偶填充规则:

    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)
    
  • 夹子

    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()
    

  • 嗨,罗伯。我想在联合体的内侧边缘周围模拟辉光效果。这是通过剪辑和阴影模糊完成的,但是我需要能够首先剪辑联盟的形状。Rob,感谢所有的帮助,以及产生这种效果的代码。这很好用!这很好,我如何用背景色填充这些形状之外的区域?假设我想要一个
    [uicolorWithWhite:0.000 alpha:0.750]
    作为半透明背景,并将这些圆圈切掉?@jamone你需要将其作为一个新问题发布,而不是作为评论。我很惊讶这个问题被浏览了多少次。我也需要同样的效果!路径本身也带有半透明的“填充”。