我需要在用户的照片周围画一个阴影。我通过绘制圆圈然后剪切上下文来绘制这些圆形照片。这是我的代码片段:
+ (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;
}
这种方法的问题显然是它影响了我的应用程序的性能 - 绘制两次以上的圆圈以及它的tableview。 我的问题是如何在剪切上下文后避免绘制第二个圆圈并绘制阴影?我确实保存并恢复了状态,但它没有帮助,我可能做错了。我还假设drawInRect关闭当前路径,这就是为什么影子不知道在哪里绘制自己。我应该再调用CGContextAddEllipseInRect然后绘制阴影吗?
答案 0 :(得分:3)
表现很有趣,但往往非常不直观。
开发人员(包括我自己)经常会感觉更快或更有效,但很少这么简单。实际上,它是在使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()
}
有几点需要注意:
此代码不扩展图像的大小以考虑阴影。您要么只想在绘制图像时扩展尺寸(导致带阴影和不带阴影的图像尺寸不同),要么总是扩大尺寸(导致图像周围没有阴影的空白区域)。您可以选择哪种行为最适合您。
对于可能的替代方案及其假设的性能差异,这是一个有趣的推测。这些建议并不是严格意义上的,而是为了说明没有单一的“正确”解决方案。
绘制的阴影始终是相同的,因此您可以通过仅绘制阴影图像然后重新使用它来假设交换CPU周期的内存。更进一步,您甚至可以为阴影包含一个资产(以更大的捆绑包和从磁盘读取的时间为代价),这样您甚至不必在第一次绘制它。
带阴影的图像仍然是透明的,这意味着它必须与背景混合(注意:混合对于今天的硬件来说几乎不是问题。这是更假设的。)。您可以将背景颜色参数传递给函数,并使其生成不透明图像。
削减图像需要付出代价。如果生成的图像不透明,则可能包含一个资源,其中包含背景,圆形和预先渲染的圆形切口(下方呈现在橙色背景上)。这样,可以将轮廓图像绘制到图像上下文中而不进行剪切,并且阴影图像将被绘制在其上方。
混合仍然在CPU上发生。通过在上面添加带有预渲染阴影和背景剪切的第二层,可以将混合工作从CPU移动到GPU。
等等......
另一个方向是层配置。您可以使用图层的圆角半径对图像进行圆角处理,并使用各种阴影属性绘制阴影。只要您记得指定明确的shadowPath
,性能差异应该很小。
您可以通过分析验证最后一个语句,并在实际设备上构建版本。 ;)