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

dma*_*nel 16 iphone quartz-2d cgpath ios

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

如果我通过将两个圆形路径加在一起来创建CGMutablePathRef,如左图所示,是否可以获得最终的CGPathRef,它只代表右边图像所示的外边框?

谢谢你的帮助!

rob*_*off 30

你要求的是bezier路径的结合.Apple不提供任何用于计算路径联合的API.它实际上是一个相当复杂的算法.这里有几个链接:

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

你可以绘制一个相当不错的内部发光,而无需实际计算路径的并集.相反,制作一个位图.填充位图上的每个路径.你将使用它作为面具.接下来,创建一个掩码的反转图像,其中填充了联合区域之外的所有内容.你将绘制这个以使CoreGraphics在联合的内边缘绘制阴影.最后,将蒙版设置为CGContext蒙版,设置阴影参数,并绘制倒置图像.

好的,这听起来很复杂.但这就是它的样子(右边的Retina版本):

辉光 发光视网膜

它并不完美(角落太亮),但它非常好.

所以这是代码.我正在绕过UIBezierPaths而不是CGPaths,但在它们之间进行转换是微不足道的.我使用了一些UIKit函数和对象.请记住,您始终可以使用UIGraphicsPushContext和将UIKit绘制到任意CGContext UIGraphicsPopContext.

首先,我们需要一个蒙版图像.它应该是一个仅限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;
}
Run Code Online (Sandbox Code Playgroud)

这实际上是困难的部分.现在我们需要一个在掩模(路径联合)区域的任何地方都是我们的发光颜色的图像.我们可以使用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;
}
Run Code Online (Sandbox Code Playgroud)

使用这两个图像,我们可以在当前的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);
}
Run Code Online (Sandbox Code Playgroud)

为了测试它,我使用几个圆创建了一个图像并将其放在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();
}
Run Code Online (Sandbox Code Playgroud)