2016-04-21 116 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

我很高興它有幫助。關鍵是,這種表現可能不直觀,而且依情況而定。 –