参考文献:http://blog.csdn.net/huli870715/article/details/39378349
http://blog.csdn.net/grp0916/article/details/50494712
1. RenderScript
RenderScript是由Android3.0引入,用来在Android
上编写高性能代码的一种语言(使用C99标准)。RenderScript有类似于CUDA的Compute
API用于计算,配置和设置相对比较容易。最终的运行速度实际上要比胜过于NDK的实现方式,需要编写的代码比较少。而RenderScript最适合用于做3D的用户界面或高性能计算任务。目前android内置API。实例代码如下:
privatevoid applyBlur() {
image.getViewTreeObserver().addOnPreDrawListener(newViewTreeObserver.OnPreDrawListener()
{
@Override
publicbooleanonPreDraw()
{
image.getViewTreeObserver().removeOnPreDrawListener(this);
image.buildDrawingCache();
Bitmap bmp = image.getDrawingCache();
blur(bmp,
text,true);
returntrue;
}
});
}
privatevoid
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();
}
当ImageView开始加载背景图时,取出它的drawableCache,进行blur处理,Gaussian
blur的主要逻辑在blur函数中。对于在Java中使用RenderScript,对应到我们的代码,步骤为:
初始化一个RenderScript Context.
至少创建一个Allocation对象用于存储需要处理的数据.
创建compute
kernel的实例,本例中是内置的ScriptIntrinsicBlur对象.
设置ScriptIntrinsicBlur实例的相关属性,包括Allocation,
radius等.
开始blur操作,对应(forEach).
将blur后的结果拷贝回bitmap中。
总结:
由于Android假设每一帧的处理时间不能超过16ms(屏幕刷新频率60fps),因此,若在主线程里执行RenderScript操作,可能会造成卡顿现象。最好的方式是将其放入AsyncTask中执行。
2. FastBlur
由于高斯模糊归根结底是像素点的操作,也许在java层可以直接操作像素点来进行模糊化处理。google一下,果不其然,一个名为stackblur的开源项目提供了名为fastBlur的方法在java层直接进行高斯模糊处理。https://github.com/kikoso/android-stackblur
这里,仅仅是把RenderScript相关的操作换成了FastBlur提供的api。
privatevoidblur(Bitmap
bkg, View view) {
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);
overlay =
FastBlur.doBlur(overlay,
(int)radius,true);
view.setBackground(new
BitmapDrawable(getResources(),overlay));
}
结论:效果很好,但是花费时间多出好多,很难接受……。
那我们继续改进:先通过缩小图片,使其丢失一些像素点,接着进行模糊化处理,然后再放大到原来尺寸。由于图片缩小后再进行模糊处理,需要处理的像素点和半径都变小,从而使得模糊处理速度加快。
privatevoidblur(Bitmap
bkg, View view) {
long
startMs = System.currentTimeMillis();
float
radius = 2;
float
scaleFactor = 8;
Bitmap
overlay =
Bitmap.createBitmap((int)(view.getMeasuredWidth()/scaleFactor),(int)(view.getMeasuredHeight()/scaleFactor),Bitmap.Config.ARGB_8888);
Canvas
canvas =new
Canvas(overlay);
canvas.translate(-view.getLeft()/scaleFactor,-view.getTop()/scaleFactor);
canvas.scale(1 / scaleFactor, 1 /scaleFactor);
Paint paint
=new
Paint();
paint.setFlags(Paint.FILTER_BITMAP_FLAG);
canvas.drawBitmap(bkg, 0, 0, paint);
overlay =
FastBlur.doBlur(overlay,
(int)radius,true);
view.setBackground(new
BitmapDrawable(getResources(),overlay));
statusText.setText("cost " +
(System.currentTimeMillis()- startMs)
+"ms");
}
代码所创建的bitmap为原图的1/8大小,接着,同样使用fastBlur来进行模糊化处理,最后再为textview设置背景,此时,背景图会自动放大到初始大小。注意,由于这里进行了缩放,radius的取值也要比之前小得多(这里将原始取值除以8得到近似值2)
结论:效果一样,处理速度却快得惊人。它相对于renderScript方案来说,节省了拷贝bitmap到Allocation中,处理完后再拷贝回来的时间开销。
但是,由于FastBlur是将整个bitmap拷贝到一个临时的buffer中进行像素点操作,因此,它不适合处理一些过大的背景图