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
- <?xml version="1.0" encoding="utf-8"?>
- <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
- <ImageView
- android:id="@+id/picture"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:src="@drawable/splash"
- android:scaleType="centerCrop" />
- <TextView
- android:id="@+id/text"
- android:gravity="center_horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="Gaussian Blur"
- android:textColor="@android:color/black"
- android:layout_gravity="center_vertical"
- android:textStyle="bold"
- android:textSize="48sp" />
- <LinearLayout
- android:id="@+id/controls"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="#7f000000"
- android:orientation="vertical"
- android:layout_gravity="bottom" />
- </FrameLayout>
核心代码:
[java] view plain copy
- private void applyBlur()
- image.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()
- @Override
- public boolean onPreDraw()
- image.getViewTreeObserver().removeOnPreDrawListener(this);
- image.buildDrawingCache();
- Bitmap bmp = image.getDrawingCache();
- blur(bmp, text, true);
- return true;
- );
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
- private void blur(Bitmap bkg, View view)
- long startMs = System.currentTimeMillis();
- float radius = 20;
- Bitmap overlay = Bitmap.createBitmap((int)(view.getMeasuredWidth()), (int)(view.getMeasuredHeight()), Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(overlay);
- canvas.translate(-view.getLeft(), -view.getTop());
- canvas.drawBitmap(bkg, 0, 0, null);
- RenderScript rs = RenderScript.create(SecondActivity.this);
- Allocation overlayAlloc = Allocation.createFromBitmap(rs, overlay);
- ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(rs, overlayAlloc.getElement());
- blur.setInput(overlayAlloc);
- blur.setRadius(radius);
- blur.forEach(overlayAlloc);
- overlayAlloc.copyTo(overlay);
- view.setBackground(new BitmapDrawable(getResources(), overlay));
- rs.destroy();
- statusText.setText("cost " + (System.currentTimeMillis() - startMs) + "ms");
当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 kernel
在JELLY_BEAN_MR1
中引入,为了在低版本手机中使用这些特性,我们不得不引入renderscript_v8兼容包,对于手Q安装包增量的硬性指标,貌似只能放弃JELLY_BEAN_MR1
以下的用户?
有点不甘心,想想别的解决方案吧。
2. FastBlur
由于高斯模糊归根结底是像素点的操作,也许在java层可以直接操作像素点来进行模糊化处理。google一下,果不其然,一个名为stackblur
的开源项目提供了名为fastBlur
的方法在java层直接进行高斯模糊处理。
项目地址请猛戳: stackblur
ok,现在来改造我们的程序.
[java] view plain copy
- private void blur(Bitmap bkg, View view)
- long startMs = System.currentTimeMillis();
- float radius = 20;
- CSS 背景图片虚化