Android 高斯模糊之RenderScript

前言:近年来,app端的图片高斯模糊效果备受那些设计师们的青睐,在那些牛逼的APP中,如微信、QQ、网易云音乐等等都有对背景做高斯图模糊的设计,我们的项目设计师也提出了这个要求,我就查了一些文档做了一下,所以有了这篇文章的由来。

目前 咱们Android 上实现高斯模糊效果的方式有以下几种:

  • Java : FastBlur.java ,是由应用非常广泛的 StackBlur 模糊算法实现代码,但是效率最低

  • C++ :有两种实现,1:标准高斯模糊算法; 2:均值模糊,效率属于中等。

  • Android : RenderScript ,用来在 Android 上编写高性能代码的一种语言(使用C99标准,运行时机器再次优化编译, 可以均衡的运行在多个CPU 和 GPU上,有一个半径限制小于25的限制),运算效率最高

这里先给大家上一张高斯模糊后的效果图差异图瞅瞅:

Android 高斯模糊之RenderScript_第1张图片

接下来我将分别简单分析一下这些实现方式各自的优缺点:

RenderScript 优点:

  • 使用简单,原生的API,十行左右的代码就能完成高斯模糊;
  • 运算效率较高,是在c/c++层做处理;

RenderScript 缺点:

  • 版本限制,API 17以上才能使用;
  • 如果使用兼容包的话,会导致APK 体积增大,support包约160k;

fastBlur的优点:

  • 没有兼容版本的问题;
  • 不需要引入三方包,不会增加APK大小;

fastBlur的缺点:

  • 运算效率很低,因为是在Java层做处理;
  • 实现方式是将Bitmap全部加载到内存,较大图片容易OOM;

往下我们接着看一下使用RenderScript和fastBlur 以及实现方式优化后,对于高斯模糊一张图片所花时间的对比表,测试机型为魅族metal,系统为Android 5.1(此图为网上复制而来,在此表示感谢):

Android 高斯模糊之RenderScript_第2张图片

正如上图显示:以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

 

你可能感兴趣的:(学无止境)