場景
經常看到各種高效裁剪圓角的文章,正好之前做過一點數字圖像處理,就打算用空域處理的辦法,寫個裁剪圓角的算法,一定要盡可能的快的,不然界面容易卡頓。
裁圓角很簡單,對於圖像上的一個點(x, y),判斷其在不在圓角矩形內,在的話 alpha 是原值,不在的話 alpha 設為 0 即可。如下圖
我遍歷所有像素,判斷每個像素在不在4個圓的圓內就行了,4個角,每個角有一個四分之一的圓。
一個優化就是,我不需要遍歷全部的像素就能裁出圓角,只需要考慮類似左下角三角形的區域就行了,左下,左上,右上,右下,一共4個三角形區域(另外3個圖中沒畫出),for循環的時候,就循環這個4個三角形區域就行了。
所以對於一幅 w * h 的圖像,設圓角大小為 n,n <= min(w, h) / 2,其復雜度為 O(n) = 2(n^2),最壞的情況計算量也不會超過 wh / 2
對於一個像素點(x, y),判斷其在不在圓內的公式
如果 (x-cx)^2 + (y-cy)^2 <= r^2 就表示點 (x, y) 在圓內,反之不在。
理論說完了,下面看實際的測試數據。
測試結果與分析
根據上面的分析,我寫了一個裁剪圓角的程序,叫為 my裁剪。
還用了蘋果 CoreGraphics 庫的 CGContext 裁剪圓角,叫為 CGContext 裁剪。
還用了 UIKit 的 UIBezierPath 裁剪圓角,叫為 貝塞爾裁剪。
下面來對比三種方法,哪種最快,分別是
1.my裁剪
2.CGContext裁剪
3.貝塞爾裁剪
實驗數據:
一張 png 格式 512 * 512 的 lena 女神的標准實驗圖像。
圓角大小分別取 10,50,100,250,這4個值。
每次實驗裁剪 10000 張圖片數據,獲得總耗時。
因為圖片是 512 * 512 的,最大的圓角為 512 / 2 = 256,所以超過 256 的會被強制設在 256,所以實驗中用了個近似 256 的 250 做為最大的測試數據。
實驗前關閉所有比較耗CPU的軟件。實驗中不操作電腦,避免影響實驗結果准確性,最好真機測試,關掉後台所有APP。最後得到了以下實驗數據,並繪制成表格。
表格.jpeg
根據上圖可以看出 my 裁剪運行時間看起來像指數上升的,是不是會更慢?
答:不是的,看 x 軸的坐標間距,10 到 50 到 100,然後直接跳到 250 了,不是均等分的!要是畫均等分的話,圖會非常非常長。
如果要 x, y 軸刻度均等分的話,畫出來的圖大概是這樣的,如下圖:
表格2.jpeg
my 裁剪時間隨著圓角大小線性上升,到達 256 的時候,是最大值了。
實驗過程中的具體數據。
圓角為 10 的情況
圓角為 50 的情況
圓角為 100 的情況
圓角為 250 的情況
結論與分析
從上面數據可以看出:
時間上:不管圓角大小 n 是多少,CGContext 和 UIBezierPath,耗時都在 14.6 秒左右。而 my裁剪在圓角小的時候,性能較好,耗時在 3 秒左右,隨著圓角增到250,耗時也去到了 12 秒,但最壞不會超過 w * h / 2,在 n < min(w, h) / 2 時,具有較高的性能,比CGContext, UIBezierPath要快。
空間上:內存使用上,沒精確測量,大致看了一下,裁剪1萬張 512 * 512的圖片,3種算法的內存使用都在 10MB 左右,還可以接受,但 UIBezierPath 裁剪時居然會寫磁盤。
另外,在圖像編碼/解碼中,用了 CGDataProviderRef,CGImageRef,這兩個對象,它們的速度應該是很快的了,如果自己寫的話,預測可以更快。
最後口說無憑,我發個代碼可以自己下載運行看看結果(下載地址)
使用方法都在代碼的 ViewController.m 文件裡,就這麼一個文件,很好找,沒有其他了。請在 iphone6,6 plus 的屏幕下運行。本實驗重點在於3個算法,無關緊要的東西沒做太多處理。