android--毛玻璃效果(背景虚化)的实现

Posted Navan-3

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android--毛玻璃效果(背景虚化)的实现相关的知识,希望对你有一定的参考价值。

----------------------------------------------------------------------------------------------------------------------------------------------------------------------

我的步骤: 1、获取srcImage图片并设置为背景。 2、将srcImage图片 虚化完成后返回Bitmap类。 3、将Bitmap(虚化的背景图片)剪裁一块出来 4、将建材出来的一块虚化背景图片设置在srcImage背景上对应的位置。 5、这样一张图片上一部分位置可以显示虚化的效果
tips:
// 将srcImage缩放成屏幕大小的图片 bmp = Bitmap.createScaledBitmap(bmp, Blur.getScreenWidth(this),Blur.getScreenHeight(this), false); // 将srcImage缩放成屏幕大小的图片 虚化 bmp = Blur.fastblur(getApplication(), bmp, 12); // 将虚化后的图片剪裁出一个矩形 bmp = Blur.clipImage(bmp, 0, 0, bmp.getWidth(), 200);

----------------------------------------------------------------------------------------------------------------------------------------------------------------------


1. RenderScript

谈到高斯模糊,第一个想到的就是RenderScript。RenderScript是由android3.0引入,用来在Android上编写高性能代码的一种语言(使用C99标准)。 引用官方文档的描述:

RenderScript runtime will parallelize work across all processors available on a device, such as multi-core CPUs, GPUs, or DSPs, allowing you to focus on expressing algorithms rather than scheduling work or load balancing.

为了在Android中使用RenderScript,我们需要(直接贴官方文档,比直译更通俗易懂):

  • High-performance compute kernels are written in a C99-derived language.
  • A Java API is used for managing the lifetime of RenderScript resources and controlling kernel execution.

学习文档:http://developer.android.com/guide/topics/renderscript/compute.html

上面两点总结成一句话为:我们需要一组compute kernels(.rs文件中编写),及一组用于控制renderScript相关的java api(.rs文件自动生成为java类)。 由于compute kernels的编写需要一定的学习成本,从JELLY_BEAN_MR1开始,Androied内置了一些compute kernels用于常用的操作,其中就包括了Gaussian blur

下面,通过实操来讲解一下RenderScript来实现高斯模糊,最终实现效果(讲文字背景进行模糊处理):

布局:

[html]  view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3. android:layout_width="match_parent"  
  4. android:layout_height="match_parent" >  
  5.   
  6.     <ImageView   
  7.         android:id="@+id/picture"   
  8.         android:layout_width="match_parent"   
  9.         android:layout_height="match_parent"   
  10.         android:src="@drawable/splash"   
  11.         android:scaleType="centerCrop" />  
  12.   
  13.     <TextView   
  14.         android:id="@+id/text"  
  15.         android:gravity="center_horizontal"   
  16.         android:layout_width="match_parent"  
  17.         android:layout_height="wrap_content"  
  18.         android:text="Gaussian Blur"  
  19.         android:textColor="@android:color/black"  
  20.         android:layout_gravity="center_vertical"  
  21.         android:textStyle="bold"  
  22.         android:textSize="48sp" />  
  23.   
  24.     <LinearLayout   
  25.         android:id="@+id/controls"   
  26.         android:layout_width="match_parent"   
  27.         android:layout_height="wrap_content"   
  28.         android:background="#7f000000"   
  29.         android:orientation="vertical"  
  30.         android:layout_gravity="bottom" />  
  31. </FrameLayout>  

核心代码:

[java]  view plain copy
  1. private void applyBlur()   
  2.     image.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()   
  3.   
  4.         @Override  
  5.         public boolean onPreDraw()   
  6.             image.getViewTreeObserver().removeOnPreDrawListener(this);  
  7.             image.buildDrawingCache();  
  8.             Bitmap bmp = image.getDrawingCache();  
  9.             blur(bmp, text, true);  
  10.             return true;  
  11.           
  12.     );  
  13.   
  14.   
  15. @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)  
  16. private void blur(Bitmap bkg, View view)   
  17.     long startMs = System.currentTimeMillis();  
  18.     float radius = 20;  
  19.   
  20.     Bitmap overlay = Bitmap.createBitmap((int)(view.getMeasuredWidth()), (int)(view.getMeasuredHeight()), Bitmap.Config.ARGB_8888);  
  21.     Canvas canvas = new Canvas(overlay);  
  22.     canvas.translate(-view.getLeft(), -view.getTop());  
  23.     canvas.drawBitmap(bkg, 00null);  
  24.   
  25.     RenderScript rs = RenderScript.create(SecondActivity.this);  
  26.   
  27.     Allocation overlayAlloc = Allocation.createFromBitmap(rs, overlay);  
  28.     ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(rs, overlayAlloc.getElement());  
  29.     blur.setInput(overlayAlloc);  
  30.     blur.setRadius(radius);  
  31.     blur.forEach(overlayAlloc);  
  32.     overlayAlloc.copyTo(overlay);  
  33.     view.setBackground(new BitmapDrawable(getResources(), overlay));  
  34.     rs.destroy();  
  35.   
  36.     statusText.setText("cost " + (System.currentTimeMillis() - startMs) + "ms");  
  37.   

当ImageView开始加载背景图时,取出它的drawableCache,进行blur处理,Gaussian blur的主要逻辑在blur函数中。对于在Java中使用RenderScript,文档中也有详细描述,对应到我们的代码,步骤为:

  • 初始化一个RenderScript Context.
  • 至少创建一个Allocation对象用于存储需要处理的数据.
  • 创建compute kernel的实例,本例中是内置的ScriptIntrinsicBlur对象.
  • 设置ScriptIntrinsicBlur实例的相关属性,包括Allocation, radius等.
  • 开始blur操作,对应(forEach).
  • 将blur后的结果拷贝回bitmap中。

此时,我们便得到了一个经过高斯模糊的bitmap。 

从上图可以看到,模糊处理花费了38ms(测试机为小米2s),由于Android假设每一帧的处理时间不能超过16ms(屏幕刷新频率60fps),因此,若在主线程里执行RenderScript操作,可能会造成卡顿现象。最好的方式是将其放入AsyncTask中执行。

此外,RenderScript在3.0引入,而一些内置的compute kernelJELLY_BEAN_MR1中引入,为了在低版本手机中使用这些特性,我们不得不引入renderscript_v8兼容包,对于手Q安装包增量的硬性指标,貌似只能放弃JELLY_BEAN_MR1以下的用户?

有点不甘心,想想别的解决方案吧。

2. FastBlur

由于高斯模糊归根结底是像素点的操作,也许在java层可以直接操作像素点来进行模糊化处理。google一下,果不其然,一个名为stackblur的开源项目提供了名为fastBlur的方法在java层直接进行高斯模糊处理。

项目地址请猛戳: stackblur

ok,现在来改造我们的程序.

[java]  view plain copy
  1. private void blur(Bitmap bkg, View view)   
  2.     long startMs = System.currentTimeMillis();  
  3.     float radius = 20;  
  4. CSS 背景图片虚化

    Android 弹窗毛玻璃背景实践

    Android 弹窗毛玻璃背景实践

    opencv图像处理之在手机上实现背景虚化

    实现毛玻璃效果背景,用做头像背景

    高斯模糊效果