天天看點

Android 高斯模糊之RenderScript

前言:近年來,app端的圖檔高斯模糊效果備受那些設計師們的青睐,在那些牛逼的APP中,如微信、QQ、網易雲音樂等等都有對背景做高斯圖模糊的設計,我們的項目設計師也提出了這個要求,我就查了一些文檔做了一下,是以有了這篇文章的由來。

目前 咱們Android 上實作高斯模糊效果的方式有以下幾種:
  • Java : FastBlur.java ,是由應用非常廣泛的 StackBlur 模糊算法實作代碼,但是效率最低。
  • C++ :有兩種實作,1:标準高斯模糊算法; 2:均值模糊,效率屬于中等。
  • Android : RenderScript ,用來在 Android 上編寫高性能代碼的一種語言(使用C99标準,運作時機器再次優化編譯, 可以均衡的運作在多個CPU 和 GPU上,有一個半徑限制小于25的限制),運算效率最高。

這裡先給大家上一張高斯模糊後的效果圖差異圖瞅瞅:

Android 高斯模糊之RenderScript

接下來我将分别簡單分析一下這些實作方式各自的優缺點:

RenderScript 優點:
  • 使用簡單,原生的API,十行左右的代碼就能完成高斯模糊;
  • 運算效率較高,是在c/c++層做處理;
RenderScript 缺點:
  • 版本限制,API 17以上才能使用;
  • 如果使用相容包的話,會導緻APK 體積增大,support包約160k;
fastBlur的優點:
  • 沒有相容版本的問題;
  • 不需要引入三方包,不會增加APK大小;
fastBlur的缺點:
  • 運算效率很低,因為是在Java層做處理;
  • 實作方式是将Bitmap全部加載到記憶體,較大圖檔容易OOM;

往下我們接着看一下使用RenderScript和fastBlur 以及實作方式優化後,對于高斯模糊一張圖檔所花時間的對比表,測試機型為魅族metal,系統為Android 5.1(此圖為網上複制而來,在此表示感謝):

Android 高斯模糊之RenderScript

正如上圖顯示:以1080 x 1349 的圖檔為例(每一個半徑取5次的均值),使用原尺寸用兩種方法進行高斯模糊,RenderScript的效率比fastBlur高,大約快10倍,但是都超過了16ms,而使用優化方法後,使其先縮小8倍,再模糊,2種方法效率都有質的提高,RenderScript模糊時間不足5ms,fastBlur 也接近16ms,半徑為15以下小與16ms。

是以不管使用哪種方法模糊圖檔,都應該先優化,再模糊。

好了,上面啰嗦了很多,今天主要講的就是根據性能的對比我選了RenderScript做高斯模糊的實作,是以下面才會具體介紹RenderScript的使用,如果想要使用其他的實作方式,請檢視對應的資料。

先在項目引入RenderScript支援庫:

在module的build.gradle中添加

android {

    defaultConfig {
        ……
        //啟用renderscript
        renderscriptTargetApi = 18
        renderscriptSupportModeEnabled = true
    }
}
           

在java類中使用時引入相容包

import android.support.v8.renderscript.*;
           

注意:引入上面的相容庫是官方推薦的做法,目的主要是為了APP在各個版本上的相容性更好(可以支援最低API 8)。如果你不想引入相容庫,那就不做上面的操作,直接用

import android.renderscript.*;

,支援最低API 11。

在下面我先貼一段使用RenderScript自帶的圖檔模糊的代碼,幫助咱們更好的了解使用:

public static void blurByRenderScript(Bitmap bitmap,int radius, ImageView view,Context context)
{
    RenderScript rs = RenderScript.create(context);

    Allocation allocation = Allocation.createFromBitmap(rs, bitmap);

    ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(rs, allocation.getElement());
    blur.setInput(allocation);
    blur.setRadius(radius);

    blur.forEach(allocation);

    allocation.copyTo(bitmap);
    view.setImageBitmap(bitmap);

    rs.destroy();
}
           

 1. 建立一個RenderScript執行個體:是用

.create()

方法建立執行個體,有好幾個重載的方法可以選。同一時間點最好隻建立一個RenderScript執行個體。

RenderScript rs = RenderScript.create(context);
           

2. 建立一個或多個Allocation類執行個體:Allocation類用于存儲需要進行處理的對象資料。包含很多靜态建立方法,比較常用的是

createFromBitmap(…)

createTyped(…)

Allocation allocation = Allocation.createFromBitmap(rs, bitmap);
           

3. 建立需要使用的腳本,這裡腳本需要分成兩類:

  • ScriptC   這是我們自己編寫的

    .rs

    檔案。編譯器會為我們自動映射一個java類,名字是

    ScriptC_檔案名

    。假如我們寫的檔案是

    abc.rs

    ,映射的類名就是

    ScriptC_abc

    ,執行個體化操作如下:
ScriptC_abc abc = new ScriptC_abc(rs);
           
  • ScriptIntrinsic   這是RenderScript内置的已經幫我們寫好了的常用腳本,比如有高斯模糊、圖像融合等等,它們都繼承抽象類ScriptIntrinsic。 
ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(rs,allocation.getElement());
           

4. 設定必要的腳本變量:

RenderScript内置的腳本設定參數就不用說了,主要講下我們自定義的腳本如何在java中指派。 如果在你自己寫的

.rs

檔案中需要使用到額外的參數,你可以定義全局變量,比如

int xyz;

,那麼自動映射的java類中将會自動産生get和set方法,如

set_xyz(int);

。這樣就可以在java代碼中做指派操作,你需要做的僅僅是定義全局變量。 

注意:靜态變量和常量不會生成set方法,隻有get。

5. 執行腳本:

最終執行一般調用

forEach

方法。在自定義的

.rs

檔案中,有參數的方法都會映射兩個java方法

forEach_方法名

,一個是參數跟你自己定義相同的方法,另一個重載的方法是多了一個Script.LaunchOptions類型的參數,可以設定x、y、z三個次元的起點和結束點(比如可以針對圖檔的某個區域進行操作)。

6. 從Allocation複制資料:将計算完成的資料從Allocation中複制出來,調用

copyto(…)

方法。 (例如複制到一個bitmap)

allocation .copyTo(bitmap);
           

7. 銷毀RenderScript執行個體:最後,當然不要忘記銷毀RenderScript執行個體,調用它的

destroy()

方法銷毀。

好了,see you

繼續閱讀