2016-04-21 72 views
0

我需要在用户的照片周围绘制阴影。我通过绘制一个圆圈然后裁剪上下文来绘制这些圆圈照片。这里是我的代码片段:剪辑后无法绘制阴影

+ (UIImage*)roundImage:(UIImage*)img imageView:(UIImageView*)imageView withShadow:(BOOL)shadow 
{ 
    UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, [UIScreen mainScreen].scale); 
    CGContextRef context = UIGraphicsGetCurrentContext(); 
    CGContextAddEllipseInRect(context, CGRectMake(0,0, imageView.width, imageView.height)); 
    CGContextSaveGState(context); 
    CGContextClip(context); 
    [img drawInRect:imageView.bounds]; 
    CGContextRestoreGState(context); 
    if (shadow) { 
     CGContextSetShadowWithColor(context, CGSizeMake(0, 0), 5, [kAppColor lighterColor].CGColor); 
    } 
    CGContextDrawPath(context, kCGPathFill); 
    UIImage* roundImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 
    return roundImage; 
} 

但是在裁剪区域后,我无法在下面绘制阴影。所以我不得不在照片后面画一个阴影。

+ (UIImage *)circleShadowFromRect:(CGRect)rect circleDiameter:(CGFloat)circleDiameter shadowColor:(UIColor*)color 
{ 
    UIGraphicsBeginImageContextWithOptions(rect.size, NO, [UIScreen mainScreen].scale); 
    CGContextRef context = UIGraphicsGetCurrentContext(); 
    CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); 
    CGFloat circleStartPointX = CGRectGetMidX(rect) - circleDiameter * 0.5; 
    CGFloat circleStartPointY = CGRectGetMidY(rect) - circleDiameter * 0.5; 
    CGContextAddEllipseInRect(context, CGRectMake(circleStartPointX,circleStartPointY, circleDiameter, circleDiameter)); 
    CGContextSetShadowWithColor(context, CGSizeMake(0, 0), 5, color.CGColor); 
    CGContextDrawPath(context, kCGPathFill); 
    UIImage *circle = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 
    return circle; 
} 

这种方法的问题显然是它击中我的应用程序的性能 - 拉两次圈加它的实现代码如下。 我的问题是我怎样才能避免绘制第二个圆,并在裁剪上下文后绘制阴影?我确实保存并恢复了状态,但没有帮助,我可能做错了。我还假定drawInRect关闭当前路径,这就是为什么阴影不知道在哪里绘制自己。我应该再次调用CGContextAddEllipseInRect然后绘制阴影吗?

enter image description here

回答

2

关于业绩

性能是有趣的,但往往是非常不直观。

包括我自己在内的开发人员通常对速度更快或效率更高的感觉,但很少那么简单。实际上,这是CPU,GPU,消耗内存,代码复杂度等等之间的折衷。

任何“性能差的代码”都会在上述其中一个瓶颈中出现,任何没有针对该瓶颈的改进都不会“提高代码的性能”。哪一个最终成为瓶颈将因情况而异,甚至可能因设备而异。即使有丰富的经验,也很难预测哪个因素是瓶颈。唯一可以肯定的方法是在每次更改之前和之后测量(阅读:使用仪器)跨越实际设备。

绘制同一图像

这就是说,你可以改变上面的代码绘制阴影相同的图像为圆形图像中的影子,但它具有裁剪之前发生。

下面是你的代码的Swift版本,就是这么做的。

func roundedImage(for image: UIImage, bounds: CGRect, withShadow shadow: Bool) -> UIImage { 
    UIGraphicsBeginImageContextWithOptions(bounds.size, false, 0.0) 
    defer { 
     UIGraphicsEndImageContext() 
    } 

    let context = UIGraphicsGetCurrentContext() 
    let circle = CGPathCreateWithEllipseInRect(bounds, nil) 

    if shadow { 
     // draw an elliptical shadow 
     CGContextSaveGState(context) 

     CGContextSetShadowWithColor(context, .zero, 5.0, UIColor.blackColor().CGColor) 
     CGContextAddPath(context, circle) 
     CGContextFillPath(context) 

     CGContextRestoreGState(context) 
    } 

    // clip to an elliptical shape, and draw the image 
    CGContextAddPath(context, circle) 
    CGContextClip(context) 
    image.drawInRect(bounds) 

    return UIGraphicsGetImageFromCurrentImageContext() 
} 

有几件事情需要注意:

  • 传递0.0在设备的主屏幕的比例因子的比例因子的结果。
  • 绘制阴影时会保存和恢复上下文,以便在绘制图像时不再计算阴影。

此代码不是扩展图像的大小以说明影子。您只需要在绘制图像时扩大尺寸(导致不同图像尺寸有或没有阴影)或始终扩大尺寸(导致图像周围没有阴影的空白空间)。这取决于你选择哪种行为最适合你。

替代考虑

这更多的是关于可能的选择和他们的假设性能差异一个有趣的猜测。这些建议并不是严格意义上的,更多的是为了说明没有单一的“正确”解决方案。

被绘制的阴影始终是相同的,因此您可以假设性地交换内存的CPU周期,只需绘制阴影图像并重新使用即可。进一步说,你甚至可以为影子包含一个资产(以一个更大的包和从磁盘读取的时间为代价),这样你甚至不需要第一次绘制它。

带阴影的图像仍然是透明的,这意味着它将不得不与背景混合(注意:混合几乎从未是今天的硬件的问题,这是更多的假设,)。您可以将背景颜色参数传递给函数,并让它生成不透明的图像。

需要剪切图片。如果生成的图像是不透明的,它可能会包含一个资产,其中包含背景,圆形和预渲染的圆形切割(下面呈现橙色背景顶部)。这样,配置文件图像可以被绘制到图像上下文中而不会被剪裁,阴影图像将被绘制在其上方。

enter image description here

的混合仍然发生在CPU上。通过添加上面预渲染阴影和背景切片的第二层,混合工作可以从CPU移动到GPU。

等等......


另一个方向去这是层配置。您可以使用图层的角半径对图像进行四舍五入,并使用各种阴影属性绘制阴影。只要您记得指定明确的shadowPath,性能差异应该很小。

您可以通过分析这两种替代方案来验证上一条语句是否包含在真实设备上的版本。 ;)

+0

谢谢先生,我一直在等你过去几天的答案。甚至在Twitter上发布了这个问题;)我一直在关注你的博客和你在过去一个月的SO上的答案:)。我是Core Graphics的新手,你的专业知识对我很有帮助。再次感谢您提供丰富的答案。现在我知道我出错了。 – macL0vin

+1

我很高兴它有帮助。关键是,这种表现可能不直观,而且依情况而定。 –