從 iOS 7 開始 Apple 從 擬物化 過渡到了 扁平化 的設計風格,同時也搭配使用了 毛玻璃風格 當作背景效果,不得不說十分驚豔,很有當時pc上 Widows Vista 和 OS X Yosemite 的味道,在那以後,Google 也從 Android L(5.0)開始使用了 原質化設計(Material Design) 設計語言,與 Microsoft 的 Metro 那種純扁平化風格看似很相像,但實則由於引用了 Z軸 的概念,使其有了陰影和立體感,傳達了 響應式交互 的設計理念。說到這裏有一些跑題,由於筆者對設計美學很感興趣,因此對這些平臺都稍微瞭解一些皮毛。今天就來研究一下如何在 Android 上實現高斯模糊效果。html
Java : FastBlur.java ,應用很是普遍的 StackBlur 模糊算法實現代碼,效率最低java
C++ :兩種實現,1:標準高斯模糊算法 2:均值模糊,效率中等android
Android : RenderScript ,用來在 Android 上編寫高性能代碼的一種語言(使用C99標準,運行時機器再次優化編譯, 能夠均衡的運行在多個CPU 和 GPU上,有一個半徑限制小於25的限制),效率最高git
由於效果的實現是基於 Java 的,因此有必要先來了解一下方法如何使用。github
public static Bitmap doBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap)
能夠看出,使用方法很是簡單,傳入待虛化的bitmap、虛化程度(通常爲8)、是否重用flag,最後返回模糊後的bitmap。算法
但若是直接把一張大圖傳入進行虛化,很容易就會產生OOM內存溢出,那就意味着我只能虛化小圖,這樣才能防止內存溢出。可是我並不想換其餘圖,那麼,咱們就應該把這張圖縮小。網絡
平時咱們對圖片縮小,必然會帶來很明顯的清晰度的損失,但高斯模糊自己的目的就是要實現模糊的效果,所以實際上的效果差異不大,幾乎能夠忽略。框架
同時因爲圖片縮小後再進行模糊處理,須要處理的像素點和半徑都變小,從而使得模糊處理速度加快。maven
public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) {}
咱們能夠利用Bitmap的 createScaledBitmap()
方法來進行bitmap的縮放。其中前三個參數很明顯,其中寬高咱們能夠選擇爲原圖尺寸的1/5;第四個filter是指縮放的效果,filter爲true則會獲得一個邊緣平滑的bitmap,反之,則會獲得邊緣鋸齒、pixelrelated的bitmap。這裏咱們要對縮放的圖片進行虛化,因此無所謂邊緣效果,filter=false。ide
因此,咱們要使用
int scaleRatio = 5;// 縮放比例 此處表明1/5 int blurRadius = 8;// 虛化程度 Bitmap scaledBitmap = Bitmap.createScaledBitmap(originBitmap, originBitmap.getWidth() / scaleRatio, originBitmap.getHeight() / scaleRatio, false); Bitmap blurBitmap = FastBlur.doBlur(scaledBitmap, blurRadius, true); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); imageView.setImageBitmap(blurBitmap);
能夠獲得以下效果:
從圖中能夠看出,首先能夠肯定思路是對的;而後,能夠看出毛玻璃效果還不是特別的明顯。爲了獲得如iOS那樣的虛化效果,咱們有兩種方法:
增大scaleRatio縮放比,使用更小的bitmap去虛化能夠獲得更好的模糊效果,並且有利於佔用內存的減少;
增大blurRadius,能夠獲得更高程度的虛化,不過會致使CPU更加intensive
這裏筆者經過增大縮放比來實驗。
scaleRatio = 10
scaleRatio = 20
經過上面對比圖咱們能夠找出最適合本身的虛化效果。
RenderScript 主要 在Android中的對圖形進行處理,RenderScript 採用C99語法進行編寫,主要優點在於性能較高。在 API 11 的時候被加入到 Android 中。同時,Google提供了
android.support.v8.renderscript
兼容包,可以實現更低版本的兼容。
RenderScript 提供了一個用於實現高斯模糊的封裝類 ScriptIntrinsicBlur ,由於在 API 17 後才正式適配到 Android ,因此在不使用兼容包的狀況下只能兼容到4.2的設備。可是,咱們有兼容包啊向下兼容不是夢。
方法很簡單,只需在build.gradle中加入:
defaultConfig { .... // 就是這麼簡單 renderscriptTargetApi 19 renderscriptSupportModeEnabled true }
另外因爲一些廠商會深度定製Android系統,因此一些必要的依賴文件會被他們直接去掉,這致使一些型號的設備上調用RenderScriptd
的部分方法時會報錯。遇到這種兼容問題的話,須要加上這些可能丟失的文件。
其實也簡單,打開android_sdk/build-tools/選擇19以上版本/renderscript/lib/packaged
咱們能夠看見3個包含.so文件的文件夾。
直接複製這三個文件加到項目工程的 jniLibs 包下,沒有的話去建一個。
若是首次建立 jniLibs 文件夾,還須要在 build.gradle 的 android{}
中加入:
sourceSets { main { jniLibs.srcDirs = ['jniLibs'] } }
針對使用的混淆的同窗,須要在混淆中加入:
-keep class android.support.v8.renderscript.** { *; }
將核心實現方法 ScriptIntrinsicBlur 封裝成工具類。
import android.support.v8.renderscript.*; // 須要導入v8包,不然沒法向下兼容 public class BlurBitmapUtil { /*** * 圖片縮放比例 (例如 1/10) */ private static int scaleRatio = 10; /** * 對圖片進行高斯模糊 * * @param context 上下文對象 * @param image 須要模糊的圖片 * @param blurRadius 模糊半徑,因爲性能限制,這個值的取值區間爲(0至25f) * @return 模糊處理後的圖片 */ public static Bitmap blurBitmap(Context context, Bitmap image, @FloatRange(from = 1, to = 25) float blurRadius) { // 計算圖片縮小後的長寬 int width = Math.round(image.getWidth() / scaleRatio); int height = Math.round(image.getHeight() / scaleRatio); // 建立一張縮小後的圖片作爲渲染的圖片 Bitmap bitmap = Bitmap.createScaledBitmap(image, width, height, false); // 建立RenderScript內核對象 RenderScript rs = RenderScript.create(context); // 建立一個模糊效果的RenderScript的工具對象,第二個參數Element至關於一種像素處理的算法,高斯模糊的話用這個就好 ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); // 因爲RenderScript並無使用VM來分配內存,因此須要使用Allocation類來建立和分配內存空間 // 建立Allocation對象的時候其實內存是空的,須要使用copyTo()將數據填充進去 Allocation input = Allocation.createFromBitmap(rs, bitmap); // 建立相同類型的Allocation對象用來輸出 Type type = input.getType(); Allocation output = Allocation.createTyped(rs, type); // 設置渲染的模糊程度, 25f是最大模糊度 blurScript.setRadius(blurRadius); // 設置blurScript對象的輸入內存 blurScript.setInput(input); // 將輸出數據保存到輸出內存中 blurScript.forEach(output); // 將數據填充到bitmap中 output.copyTo(bitmap); // 銷燬它們釋放內存 input.destroy(); output.destroy(); blurScript.destroy(); rs.destroy(); type.destroy(); return bitmap; }
使用 RenderScript 增長虛化程度的方法和 FastBlur 同樣,有兩種方法:
增大scaleRatio縮放比,使用更小的bitmap去虛化能夠獲得更好的模糊效果,並且有利於佔用內存的減少;
增大blurRadius,能夠獲得更高程度的虛化,不過會致使虛化時間變長
但由於 RenderScript 的自然優點(低級語言, 運行時機器再次優化編譯, 能夠均衡的運行在多個CPU 和 GPU上),因此這裏筆者經過增大虛化程度來實驗,縮放比例爲 1/10,實際運用時能夠根據需求在對虛化程度和縮放比例上採起一個合適的數值。
blurRadius = 5
blurRadius = 15
blurRadius = 25
經過上面對比圖咱們能夠找出最適合本身的虛化效果。
目前來看,爲什麼 Google 設置這個25的限制, 緣由應該有兩個 :
半徑大於25的話耗時就成爲了一個瓶頸;
若是想實現大於25的模糊效果,能夠經過縮小原圖,模糊,再放大來達到一樣的效果
以上就是如何用 FastBlur 和 RenderScript 在 Android 上實現和 iOS 同樣的高斯模糊效果的簡單介紹,雖然在性能上毋庸置疑是 RenderScript 上最好,可是在一些使用場景上 FastBlur 耗時會更短,因此咱們各取所需,根據實際需求去選擇使用。
上面說的2種解決方案都是從性能和效率出發的產物,但若是個人需求就是從網絡上加載一張圖片(好比頭像),而後再高斯模糊化當背景,走一遍轉換成bitma再將其轉換成高斯模糊的流程或許會有一點點麻煩,這裏我再提供一種簡單快捷的解決方案 —— 基於Glid加載框架去實現一鍵 加載網絡圖片→高斯模糊化→展現。
首先在build.gradle中加入圖片框架須要的庫和圖片工具庫:
defaultConfig { .... compile 'com.yutianran.maven:super-adapter:1.0.0' compile 'jp.wasabeef:glide-transformations:2.0.2' }
Glide.with(this).load(url).bitmapTransform(new BlurTransformation(this,25)).into(imageView);
須要的參數很分別是
上下文對象
圖片url
上下文對象,虛化數值
imageView控件
效果如上,能夠看出 glide-transformations 庫的虛化效果也是十分不錯的,但對圖片自己作的縮放應該不是不少,因此在加載速度上會弱於 FastBlur 和 RenderScript ,但做爲輕量級圖片而言足夠了。
相關代碼已上傳至Github:BlurView,歡迎Star,Fork。