Android 动态高斯模糊效果教程

写在前面

最近一直在做毕设项目的准备工作,考虑到可能要用到一个模糊的效果,所以就学习了一些高斯模糊效果的实现。比较有名的就是 FastBlur 以及它衍生的一些优化方案,还有就是今天要说的RenderScript 。

因为这东西是现在需要才去学习的,所以关于一些图像处理和渲染问题就不提了。不过在使用的过程中确实能感受到,虽然不同的方案都能实现相同的模糊效果,但是效率差别真的很大。

本篇文章实现的高斯模糊是根据下面这篇文章学习的,先推荐一下。本文内容与其内容差不多,只是稍微讲的详细一点,并修改了代码中部分实现逻辑和细节上的处理。不过主体内容不变,所以选择哪篇文章去学都是一样的。

下面就来看一下,如何去实现这样的高斯模糊效果。

简单聊聊 Renderscript

因为效果的实现是基于 Renderscript 的,所以有必要先来了解一下。

从它的官方文档来看,说的很是玄乎。我们只需要知道一点就好了:

RenderScript is a framework for running computationally intensive tasks at high performance on Android.
Renderscript 是 Android 平台上进行高性能计算的框架。
既然是高性能计算,那么说明 RenderScript 对图像的处理非常强大,所以用它来实现高斯模糊还是比较好的选择。

那么如何使用它呢?从官方文档中可以看到,如果需要在 Java 代码中使用 Renderscript 的话,就必须依赖 android.renderscript 或者android.support.v8.renderscript 中的 API 。既然有 API 那就好办多了。

下面简单说一下使用的步骤,这也是官方文档中的说明:

  1. 首先需要通过 Context 创建一个 Renderscript ;
  2. 其次通过创建的 Renderscript 来创建一个自己需要的脚本( ScriptIntrinsic ),比如这里需要模糊,那就是 ScriptIntrinsicBlur ;
  3. 然后至少创建一个 Allocation 类来创建、分配内存空间;
  4. 接着就是对图像进行一些处理,比如说模糊处理;
  5. 处理完成后,需要刚才的 Allocation 类来填充分配好的内存空间;
  6. 最后可以选择性的对一些资源进行回收。

文档中的解释永远很规矩,比较难懂,我们结合原博主 湫水长天 的代码来看一看步骤:

/**
 * @author Qiushui
 * @description 模糊图片工具类
 * @revision Xiarui 16.09.05
 */
public class BlurBitmapUtil {
  //图片缩放比例
  private static final float BITMAP_SCALE = 0.4f;

  /**
   * 模糊图片的具体方法
   *
   * @param context 上下文对象
   * @param image  需要模糊的图片
   * @return 模糊处理后的图片
   */
  public static Bitmap blurBitmap(Context context, Bitmap image,float blurRadius) {
    // 计算图片缩小后的长宽
    int width = Math.round(image.getWidth() * BITMAP_SCALE);
    int height = Math.round(image.getHeight() * BITMAP_SCALE);

    // 将缩小后的图片做为预渲染的图片
    Bitmap inputBitmap = Bitmap.createScaledBitmap(image, width, height, false);
    // 创建一张渲染后的输出图片
    Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);

    // 创建RenderScript内核对象
    RenderScript rs = RenderScript.create(context);
    // 创建一个模糊效果的RenderScript的工具对象
    ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));

    // 由于RenderScript并没有使用VM来分配内存,所以需要使用Allocation类来创建和分配内存空间
    // 创建Allocation对象的时候其实内存是空的,需要使用copyTo()将数据填充进去
    Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
    Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);

    // 设置渲染的模糊程度, 25f是最大模糊度
    blurScript.setRadius(blurRadius);
    // 设置blurScript对象的输入内存
    blurScript.setInput(tmpIn);
    // 将输出数据保存到输出内存中
    blurScript.forEach(tmpOut);

    // 将数据填充到Allocation中
    tmpOut.copyTo(outputBitmap);

    return outputBitmap;
  }
}

上面就是处理高斯模糊的代码,其中注释写的十分详细,而且已经将图片缩放处理了一下。结合刚才说的步骤,大家应该能有一个大概的印象,实在不懂也没关系,这是一个工具类,直接 Copy 过来即可。

当然,原博主将代码封装成轮子了,也可以直接在项目中引用 Gradle 也是可以的,但是我觉得源码还是要看一看的。

简单的模糊

好了,有了一个大概的印象后,来看一下如何实现高斯模糊效果吧!

首先你可以在项目中直接引用原博主封装的轮子:

compile 'com.qiushui:blurredview:0.8.1'

如果不想引用的话,就必须在当前 Module 的 build.gradle 中添加如下代码:

defaultConfig {
  renderscriptTargetApi 19
  renderscriptSupportModeEnabled true
}

等构建好就可以使用了。如果构建失败的话,只需要把 minSdkVersion 设置成 19 就好了,暂时不知是何原因。不过从 StackOverflow 中了解到这是个Bug ,那就不必深究。

现在来看代码实现,首先布局文件中就一个 ImageView ,没啥好说的,从上面的模糊图片工具类可以看出,要想获得一个高斯模糊效果的图片,需要三样东西:

Context:上下文对象
Bitmap:需要模糊的图片
BlurRadius:模糊程度

这里需要注意一下:

目前这种方案只适用于 PNG 格式的图片,而且图片大小最好小一点,虽然代码中已经缩放了图片,但仍然可能会出现卡顿的情况。

现在只要设置一下图片和模糊程度就好了:

/**
 * 初始化View
 */
@SuppressWarnings("deprecation")
private void initView() {
  basicImage = (ImageView) findViewById(R.id.iv_basic_pic);
  //拿到初始图
  Bitmap initBitmap = BitmapUtil.drawableToBitmap(getResources().getDrawable(R.raw.pic));
  //处理得到模糊效果的图
  Bitmap blurBitmap = BlurBitmapUtil.blurBitmap(this, initBitmap, 20f);
  basicImage.setImageBitmap(blurBitmap);
}

来看一下运行图:

可以看到,图片已经实现了模糊效果,而且速度还蛮快的,总的来说通过 BlurBitmapUtil.blurBitmap()就能得到一张模糊效果的图 。

自定义模糊控件

原博主的轮子里给我们封装了一个自定义的 BlurredView ,刚开始我觉得没必要自定义。后来发现自定义的原因是需要实现动态模糊效果。

那为什么不能手动去设置模糊程度呢?他给出的解释是:

“如果使用上面的代码进行实时渲染的话,会造成界面严重的卡顿。”

我也亲自试了一试,确实有点卡。他实现动态模糊处理的方案是这样的:

“先将图片进行最大程度的模糊处理,再将原图放置在模糊后的图片上面,通过不断改变原图的透明度(Alpha值)来实现动态模糊效果。”

这个方案确实很巧妙的实现动态效果,但是注意如果要使用这种方式,就必须有两张一模一样的图片。如果在代码中直接写,就需要两个控件,如果图片多的话,显然是不可取的。所以轮子里有一个自定义的 BlurredView 。

不过这个 BlurredView 封装的不是太好,我删减了一部分内容,原因稍后再说。先来看一下核心代码。

首先是自定义的 BlurredView 继承于 RelativeLayout ,在布局文件中可以看到,里面有两个 ImageView,且是叠在一起的。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="match_parent"
       android:layout_height="match_parent">

  <ImageView
    android:id="@+id/blurredview_blurred_img"
    .../>

  <ImageView
    android:id="@+id/blurredview_origin_img"
    .../>

</FrameLayout>

同时也定义了一些属性:

<resources>
  <declare-styleable name="BlurredView">
    <attr name="src" format="reference"/>
    <attr name="disableBlurred" format="boolean"/>
  </declare-styleable>
</resources>

一个是设置图片,一个是设置是否禁用模糊。最后就是 BlurredView 类,代码如下,有大量删减,只贴出核心代码:

/**
 * @author Qiushui
 * @description 自定义模糊View类
 * @revision Xiarui 16.09.05
 */
public class BlurredView extends RelativeLayout {

  /*========== 全局相关 ==========*/
  private Context mContext;//上下文对象
  private static final int ALPHA_MAX_VALUE = 255;//透明最大值
  private static final float BLUR_RADIUS = 25f;//最大模糊度(在0.0到25.0之间)

  /*========== 图片相关 ==========*/
  private ImageView mOriginImg;//原图ImageView
  private ImageView mBlurredImg;//模糊后的ImageView
  private Bitmap mBlurredBitmap;//模糊后的Bitmap
  private Bitmap mOriginBitmap;//原图Bitmap

  /*========== 属性相关 ==========*/
  private boolean isDisableBlurred;//是否禁用模糊效果

  ...

  /**
   * 以代码的方式添加待模糊的图片
   *
   * @param blurredBitmap 待模糊的图片
   */
  public void setBlurredImg(Bitmap blurredBitmap) {
    if (null != blurredBitmap) {
      mOriginBitmap = blurredBitmap;
      mBlurredBitmap = BlurBitmapUtil.blurBitmap(mContext, blurredBitmap, BLUR_RADIUS);
      setImageView();
    }
  }
  ...

  /**
   * 填充ImageView
   */
  private void setImageView() {
    mBlurredImg.setImageBitmap(mBlurredBitmap);
    mOriginImg.setImageBitmap(mOriginBitmap);
  }

  /**
   * 设置模糊程度
   *
   * @param level 模糊程度, 数值在 0~100 之间.
   */
  @SuppressWarnings("deprecation")
  public void setBlurredLevel(int level) {
    //超过模糊级别范围 直接抛异常
    if (level < 0 || level > 100) {
      throw new IllegalStateException("No validate level, the value must be 0~100");
    }

    //禁用模糊直接返回
    if (isDisableBlurred) {
      return;
    }

    //设置透明度
    mOriginImg.setAlpha((int) (ALPHA_MAX_VALUE - level * 2.55));
  }

  ...
}

从代码中可以看到,最核心的就是下面三个方法:

setBlurredImg(Bitmap blurredBitmap):设置图片,并复制两份;
setImageView():给两个ImageView设置相应的图片,内部调用;
setBlurredLevel(int level):设置透明程度;

思路就是先选定一张图片,一张作为原图,一张作为模糊处理过的图。再分别将这两张图设置给自定义 BlurredView 中的两个 ImageView ,最后处理模糊过后的那张图的透明度。
好了,现在来写一个自定义的模糊效果图,首先是布局,很简单:

 <com.blurdemo.view.BlurredView
    android:id="@+id/bv_custom_blur"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:src="@raw/pic"
    app:disableBlurred="false" />

可以看到,设置了图片,设置了开启模糊,那么我们在Activity中只需设置透明程度即可:

private void initView() {
    customBView = (BlurredView) findViewById(R.id.bv_custom_blur);
    //设置模糊度
    customBView.setBlurredLevel(100);
  }

效果图与上图一样,这里就不重复贴了。可以看到,代码简单了很多,不过仅仅因为方便简单可不是自定义 View 的作用,作用在于接下来要说的 动态模糊效果 的实现。

动态模糊

我们先来看一下啥叫动态模糊效果:

从图中可以看到,随着我们触摸屏幕的时候,背景的模糊程度会跟着变化。如果要直接设置其模糊度会及其的卡顿,所以正如原博主所说,可以用两张图片来实现。

大体思路就是,上面的图片模糊处理,下面的图片不处理,然后通过手势改变上面模糊图片的透明度即可。

所以跟前面的代码几乎一样,只需要重写 onTouchEvent 方法即可:

/**
 * 初始化View
 */
private void initView() {
  customBView = (BlurredView) findViewById(R.id.bv_dynamic_blur);
  //设置初始模糊度
  initLevel = 100;
  customBView.setBlurredLevel(initLevel);
}

/**
 * 触摸事件
 */
@Override
public boolean onTouchEvent(MotionEvent ev) {
  switch (ev.getAction()) {
    case MotionEvent.ACTION_DOWN:
      downY = ev.getY();
      break;

    case MotionEvent.ACTION_MOVE:
      float moveY = ev.getY();
      //手指滑动距离
      float offsetY = moveY - downY;
      //屏幕高度 十倍是为了看出展示效果
      int screenY = getWindowManager().getDefaultDisplay().getHeight() * 10;
      //手指滑动距离占屏幕的百分比
      movePercent = offsetY / screenY;
      currentLevel = initLevel + (int) (movePercent * 100);
      if (currentLevel < 0) {
        currentLevel = 0;
      }
      if (currentLevel > 100) {
        currentLevel = 100;
      }
      //设置模糊度
      customBView.setBlurredLevel(currentLevel);
      //更改初始模糊等级
      initLevel = currentLevel;
      break;
    case MotionEvent.ACTION_UP:
      break;
  }
  return super.onTouchEvent(ev);
}

从代码中可以看到,这里是通过手指滑动距离占屏幕的百分比来计算改变后的透明等级的,代码应该不难,很容易理解。当然原博主博客中是通过进度条来改变的,也是可以的,就不在赘述了。

与 RecylcerView 相结合

先来看一张效果图,这个图也是仿照原博主去实现的,但是还是有略微的不同。

本来的自定义 BlurredView 中还有几段代码是改变背景图的位置的,因为希望上拉下拉的时候背景图也是可以移动的,但是从体验来看效果不是太好,上拉的过程中会出现留白的问题。

虽然原博主给出了解决方案:手动给背景图增加一个高度,但这并不是最好的解决方式,所以我就此功能给删去了,等找到更好的实现方式再来补充。

现在来看如何实现?首先布局就是底下一层自定义的 BlurredView ,上面一个 RecylcerView,RecylcerView 有两个 Type ,一个是头布局,一个是底下的列表,很简单,就不详细说了。

重点仍然是动态模糊的实现,在上面的动态模糊中,我们采取了重写 onTouchEvent 方法,但是这里刚好是 RecylcerView ,我们可以根据它的滚动监听,也就是 onScrollListener 来完成动态改变透明度,核心方法如下:

 //RecyclerView 滚动监听
  mainRView.setOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
      super.onScrollStateChanged(recyclerView, newState);
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
      super.onScrolled(recyclerView, dx, dy);
      //滚动距离
      mScrollerY += dy;
      //根据滚动距离控制模糊程度 滚动距离是模糊程度的十倍
      if (Math.abs(mScrollerY) > 1000) {
        mAlpha = 100;
      } else {
        mAlpha = Math.abs(mScrollerY) / 10;
      }
      //设置透明度等级
      recyclerBView.setBlurredLevel(mAlpha);
    }
  });

代码很简单,就是在 onScrolled 方法中计算并动态改变透明度,只要掌握了原理,实现起来还是很容易的。

总结

从前面所有的动态图可以看到,运行起来还是比较快的,但是我从 Android Monitor 中看到,在每一次刚开始渲染模糊的时候,GPU 渲染的时间都很长,所以说可能在性能方面还是有所欠佳。

当然也可能跟模拟器有关系,真机上测试是很快的。而且貌似比 FastBlur 还快一点,等有空测试几个高斯模糊实现方法的性能,来对比一下。

到此,这种实现高斯模糊的方法已经全部讲完了,感谢原博主这么优秀的文章,再次附上链接:

湫水长天 – 教你一分钟实现动态模糊效果

其他参考资料

RenderScript – Android Developers

Android RenderScript入门(1)

高斯模糊效果实现方案及性能对比 – lcyFox

项目源码

BlurDemo – IamXiaRui – Github

以上就是对Android 动态高斯模糊效果教程的示例,谢谢大家对本站的支持!

(0)

相关推荐

  • Android实现动态高斯模糊效果

    高斯模糊是什么? 高斯模糊(英语:Gaussian Blur),也叫高斯平滑,是在Adobe Photoshop.GIMP以及Paint.NET等图像处理软件中广泛使用的处理效果,通常用它来减少图像噪声以及降低细节层次.这种模糊技术生成的图像,其视觉效果就像是经过一个半透明屏幕在观察图像,这与镜头焦外成像效果散景以及普通照明阴影中的效果都明显不同. 什么?看不明白?没关系,我也看不明白,维基百科复制回来的嘛.我们直接放一些图片来了解以下这个高斯模糊是怎么样的.因为高斯模糊在iOS中最常见,这里抓

  • Android 高仿微信语音聊天页面高斯模糊(毛玻璃效果)

    目前的应用市场上,使用毛玻璃效果的APP随处可见,比如用过微信语音聊天的人可以发现,语音聊天页面就使用了高斯模糊效果. 先看下效果图: 仔细观察上图,我们可以发现,背景图以用户头像为模板,对其进行了高斯模糊,并把它作为整个页面的背景色. 关于Android如何快速实现高斯模糊(毛玻璃效果),网上一堆相关介绍,可参考下面文章一种快速毛玻璃虚化效果实现–Android. 下面直接给出模糊化工具类(已验证可行): import android.graphics.Bitmap; /** * 快速模糊化工

  • Android实现图片转高斯模糊以及高斯模糊布局

    第一个为大家介绍图片如何转高斯模拟: 1.方法的实现: public static void updateBgToBlur(Activity a, Bitmap bmpToBlur, View view, int resId) { BitmapFactory.Options opt = new BitmapFactory.Options(); opt.inJustDecodeBounds = true; opt.inSampleSize = 8; opt.inJustDecodeBounds =

  • Android RenderScript高斯模糊

    看代码的时候,看到了其中有.rs结尾的文件,不是很明白,还有RenderScript类,看的一脸蒙蔽,不知所云,然后百度了一下,收货还真不少,这东西在图形处理这块用处挺大的. 今天先说说ScriptIntrinsicBlur,这个类不需要定义rs文件,从这个Intrinsic单词可以看的出来,它是API17以后内置的类,专门用来处理图像的,让图片变模糊. public static Bitmap blurBitmap(Bitmap bitmap, float radius, Context co

  • Android 实现图片模糊、高斯模糊、毛玻璃效果的三种方法

    在前几天写过一个使用glide-transformations的方法实现高斯模糊的方法,今天偶然间有发现一个大神写的另一个方法,感觉挺不错的,分享一下: 效果图: 原文链接:点击访问 这使用也很简单,导入依赖,使用模糊方法就行,就这两步搞定 依赖: compile 'net.qiujuer.genius:blur:2.0.0-beta4' 实现方法有三种,第一个是Java实现的,第二个和第三个是调用C语言实现的 ,具体的区别也就是代码执行的耗时操作时间,从图片中可以看出Java使用时间远大于c运

  • Android实现动态高斯模糊效果示例代码

     写在前面 现在,越来越多的App里面使用了模糊效果,这种模糊效果称之为高斯模糊.大家都知道,在Android平台上进行模糊渲染是一个相当耗CPU也相当耗时的操作,一旦处理不好,卡顿是在所难免的.一般来说,考虑到效率,渲染一张图片最好的方法是使用OpenGL,其次是使用C++/C,使用Java代码是效率是最低,速度也是最慢的.但是Android推出RenderScript之后,我们就有了选择,测试表明,使用RederScript的渲染效率和使用C++/C不相上下,但是使用RenderScript

  • Android 实现高斯模糊效果且兼容低版本

    一.效果演示 项目中用到了高斯模糊效果,查阅过一些资料,考虑到性能问题最终还是选择使用Android自带的RenderScript库来实现,关于使用RenderScript来实现高斯模糊网上也有很多类似的方法,大部分都总结的比较乱,此处算是做一个整理吧,供有类似需求的同学参考及学习. (项目效果图) 简单描述项目效果图的实现思路: ① 加载定义的xml的Layout ② 使用截屏方法获取当前窗口的Bitmap对象 ③ 将Bitmap对象进行压缩及高斯模糊处理 ④ 将处理过的模糊图对象作为①中所加

  • Android RenderScript实现高斯模糊

    昨天看了下RenderScript的官方文档,发现RenderScript这厮有点牛逼.无意中发现ScriptIntrinsic这个抽象类,有些很有用的子类.其中有个子类叫ScriptIntrinsicBlur类,大致就是将图片实现高斯模糊. ScriptIntrinsic的申明: ScriptIntrinsicBlur类的申明: 加上结合着看了下SDK中的samples,自己写了个高斯模糊. ( sample的具体位置为: SDK目录/samples/android-19/renderscri

  • Android关于Glide的使用(高斯模糊、加载监听、圆角图片)

    高斯模糊.加载监听.圆角图片这些相信大家都很熟悉,那如何实现这些效果,请大家参考本文进行学习. 1.引用 compile 'com.github.bumptech.glide:glide:3.7.0' 2.加载图片 2.1 基本加载 Glide.with(context)     .load(url)     .into(imageView); 2.2 设置加载中和加载失败的情况 Glide.with(context) .load(url) .placeholder(R.drawable.loa

  • Android图片特效:黑白特效、圆角效果、高斯模糊

    1.黑白效果 复制代码 代码如下: /**     * 将彩色图转换为黑白图     *      * @param 位图     * @return 返回转换好的位图     */    public static Bitmap convertToBlackWhite(Bitmap bmp) {        int width = bmp.getWidth(); // 获取位图的宽        int height = bmp.getHeight(); // 获取位图的高 int[] pi

随机推荐