Android之高斯模糊的记录
来源:互联网 发布:网络语言大大什么意思 编辑:程序博客网 时间:2024/05/21 22:22
最近在开发项目的时候遇到一个UI提出的效果就是PS里面的高斯模糊效果,上图
下面其实是一段文本内容,但是当用户没有获取到某种权限的时候,是不能查看具体的文字内容的(但是又给用户一种下面有文字内容的模糊的感觉)。当用户点击偷瞄一下的时候需要获取某种权限,使这个模糊(遮罩)效果消失,显露出真正的文字内容。UI说在PS里面这叫高斯模糊。自己网上看了看,其实在Android里面也有高斯模糊效果的API,在API 11的时候RenderScript,用来进行高效的图片处理。其实所谓的模糊,就是在以我们真实的图片的基础上,对这张照片来进行处理,然后将处理后的照片,放在真实图片上面,给人一种放否能看清又不能看清的感觉,当年的微信发红包看图片应该也是这样实现的吧,只是将我这里的偷瞄一眼改成了需要发红包,其实是异曲同工之妙吧。先来看看RenderScript对高斯模糊的操作方法吧
public static Bitmap blurBitmap(Bitmap bitmap,Context context){ //Let's create an empty bitmap with the same size of the bitmap we want to blur Bitmap outBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888); //Instantiate a new Renderscript RenderScript rs = RenderScript.create(context); //Create an Intrinsic Blur Script using the Renderscript ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); //Create the Allocations (in/out) with the Renderscript and the in/out bitmaps Allocation allIn = Allocation.createFromBitmap(rs, bitmap); Allocation allOut = Allocation.createFromBitmap(rs, outBitmap); //Set the radius of the blur blurScript.setRadius(1.f); //其实这里就是设置的模糊程度 值越大模糊越厉害 最后可能一团黑 哈哈 //Perform the Renderscript blurScript.setInput(allIn); blurScript.forEach(allOut); //Copy the final bitmap created by the out Allocation to the outBitmap allOut.copyTo(outBitmap); //recycle the original bitmap bitmap.recycle(); //After finishing everything, we destroy the Renderscript. rs.destroy(); return outBitmap; }这里高斯模糊的逻辑其实就是 ,根据你所传入的bitmap,在setRadius(int)设置模糊程度,用RenderScript调用之后 模糊之后 返回模糊之后的图片,再将我们模糊之后的bitmap作为一张照片"盖"在我们所需要模糊(保护)的内容这块区域之上,这样就给人一种神秘的感觉。这是google 给我们提供的api里面有的,在性能更方面应该是比较优的,但是当时没有选择用这种方法,而是选择了github上面的FastBlur方法,至于原因后续再说。但是正如前面所说,RenderScript是api11之后才能使用的,但是低版本怎么办呢,这就需要考虑到兼容性了,还好google也提供了一套方案。下面就兼容低版本的高斯模糊做一些配置问题。其实低版本可以将supportV8中的包拷贝进来。
1.首先去你的sdk的目录下build-tool目录下找到所需要的jar包,我的路径是 H:\android-sdk-windows\build-tools\23.0.3\renderscript\lib,将renderscript-v8.jar拷贝到我们的工程当中libs下面,以后所有的用到renderScript的相关操作的类,都引用这里面的,不要引用系统默认的。例如import android.renderscript.RenderScript; 改为 import android.support.v8.renderscript.RenderScript;
2.使用RenderScript库,在某些手机或Android版本奔溃的问题 ,错误信息:
H:/AndroidRuntime(4476): android.support.v8.renderscript.RSRuntimeException: Error loading RS
jni library: java.lang.UnsatisfiedLinkError: Couldn't load RSSupport: findLibrary returned null
导入官方jar renderscript-v8.jar 报这个错误 android.support.v8.renderscript.RSRuntimeException: Error loadin 或者 java.lang.UnsatisfiedLinkError: Couldn't load RSSupport from loader dalvik.system.PathClassLoader
这个错误原因是因为在4.4以上的手机上自带 librsjni.so和libRSSupport.so 而在4.0以下,或者某些奇葩手机是没有这两个jni 的.所以我将我H:\android-sdk-windows\build-tools\23.0.3\renderscript\lib\packaged 目录下的文件全部拷贝到lib下面对应的文件夹下面 没有对应的文件夹就创建,如果有对应的文件夹就将文件夹中的内容拷贝进去arm64-v8a,armeabi-v7a,mips,x86。
这2步进行了之后 ,我们就可以完全兼容低版本的问题了额。
在进行高斯模糊的时候 ,我们有时候会遇到这样一种需求,就是当我们的界面(Activity)显示完成之后,我们希望我们模糊效果就能出来,我上面的渲染bitmap的函数中因为函数中用到bitmap.getWidth(),getHeight()获得其宽高,那我们应该在什么时候去调用我们的这个方法 使其能够正常运行呢?在onStart()方法中?还是在onResume()回调方法中?呵呵,其实都不是。实际中做实验的时候,你可去尝试,这个两个函数中是获取不到其宽高的呢,那么下面有几种方法可以使用 ,都是我经过尝试使用之后,能够运行的。
1,如果你在Activity启动完成,界面显示出来的时候需要显示我们对某个控件的模糊效果,我们可以使用Activity的回调方法。
@Override public void onWindowFocusChanged(boolean hasFocus) { // TODO Auto-generated method stub super.onWindowFocusChanged(hasFocus); if(hasFocus){ //界面完全渲染完成 //我们应该在这里面执行我们的模糊图片的效果 }else{ //界面焦点失去 } }为什么在这个方法之中执行呢?不在上面所说的onResume()方法中呢?其实我也说不了很清楚,说说自己的理解吧,可能在onResume()回调方法当中,所有界面并没有完全渲染完成,所有有些控件我们是不能得到他的宽高等属性的,而在onWindowFcusChanged回调方法中,我们所有的UI都渲染完成,能够获得我们的宽高了,上面的blurBitmap也不会报错了。
2,view.post(runnable),将我们的模糊执行的逻辑放在runnalbe中执行,这里我的理解是,通过post将一个runnable投递到消息队列的队尾,然后等待Looper调用此runnable的时候,view已经初始化完成了。
3。ViewTreeObserver:使用ViewTreeObserver的众多回调可以完成这个功能,比如使用addOnPreDrawListener这个接口,不知道为什么这里获取宽高也不会出错。
hover_view.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {@Overridepublic boolean onPreDraw() {hover_view.getViewTreeObserver().removeOnPreDrawListener(this); //以免重复执行 所以先移除textview.buildDrawingCache();Bitmap bmp = textview.getDrawingCache();FastBlur.blur(bmp, textview, hover_view, context);return true;}});
我的具体需求是在listView中去给每一个Item拥有模糊效果,由于这个模糊涉及到bitmap,所以当数据Item很多的时候,滑动会存在卡顿现象。因为在blurBitmap函数的27行有一句代码bitmap.recycle(); 当我们listView的item 复用时候 由于bitmap已经被回收了,会造成程序崩溃的bug。所以当时我就考虑用github上面的FastBlur去实现(后来才发现其实RenderScript也可以实现)这种模糊效果。
看看gitHub上面的FastBulr的模糊效果的具体实现:
public static Bitmap doBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap) { Bitmap bitmap; if (canReuseInBitmap) { bitmap = sentBitmap; } else { bitmap = sentBitmap.copy(sentBitmap.getConfig(), true); } if (radius < 1) { return (null); } int w = bitmap.getWidth(); int h = bitmap.getHeight(); int[] pix = new int[w * h]; bitmap.getPixels(pix, 0, w, 0, 0, w, h); int wm = w - 1; int hm = h - 1; int wh = w * h; int div = radius + radius + 1; int r[] = new int[wh]; int g[] = new int[wh]; int b[] = new int[wh]; int rsum, gsum, bsum, x, y, i, p, yp, yi, yw; int vmin[] = new int[Math.max(w, h)]; int divsum = (div + 1) >> 1; divsum *= divsum; int dv[] = new int[256 * divsum]; for (i = 0; i < 256 * divsum; i++) { dv[i] = (i / divsum); } yw = yi = 0; int[][] stack = new int[div][3]; int stackpointer; int stackstart; int[] sir; int rbs; int r1 = radius + 1; int routsum, goutsum, boutsum; int rinsum, ginsum, binsum; for (y = 0; y < h; y++) { rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; for (i = -radius; i <= radius; i++) { p = pix[yi + Math.min(wm, Math.max(i, 0))]; sir = stack[i + radius]; sir[0] = (p & 0xff0000) >> 16; sir[1] = (p & 0x00ff00) >> 8; sir[2] = (p & 0x0000ff); rbs = r1 - Math.abs(i); rsum += sir[0] * rbs; gsum += sir[1] * rbs; bsum += sir[2] * rbs; if (i > 0) { rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; } else { routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; } } stackpointer = radius; for (x = 0; x < w; x++) { r[yi] = dv[rsum]; g[yi] = dv[gsum]; b[yi] = dv[bsum]; rsum -= routsum; gsum -= goutsum; bsum -= boutsum; stackstart = stackpointer - radius + div; sir = stack[stackstart % div]; routsum -= sir[0]; goutsum -= sir[1]; boutsum -= sir[2]; if (y == 0) { vmin[x] = Math.min(x + radius + 1, wm); } p = pix[yw + vmin[x]]; sir[0] = (p & 0xff0000) >> 16; sir[1] = (p & 0x00ff00) >> 8; sir[2] = (p & 0x0000ff); rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; rsum += rinsum; gsum += ginsum; bsum += binsum; stackpointer = (stackpointer + 1) % div; sir = stack[(stackpointer) % div]; routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; rinsum -= sir[0]; ginsum -= sir[1]; binsum -= sir[2]; yi++; } yw += w; } for (x = 0; x < w; x++) { rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; yp = -radius * w; for (i = -radius; i <= radius; i++) { yi = Math.max(0, yp) + x; sir = stack[i + radius]; sir[0] = r[yi]; sir[1] = g[yi]; sir[2] = b[yi]; rbs = r1 - Math.abs(i); rsum += r[yi] * rbs; gsum += g[yi] * rbs; bsum += b[yi] * rbs; if (i > 0) { rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; } else { routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; } if (i < hm) { yp += w; } } yi = x; stackpointer = radius; for (y = 0; y < h; y++) { // Preserve alpha channel: ( 0xff000000 & pix[yi] ) pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum]; rsum -= routsum; gsum -= goutsum; bsum -= boutsum; stackstart = stackpointer - radius + div; sir = stack[stackstart % div]; routsum -= sir[0]; goutsum -= sir[1]; boutsum -= sir[2]; if (x == 0) { vmin[y] = Math.min(y + r1, hm) * w; } p = x + vmin[y]; sir[0] = r[p]; sir[1] = g[p]; sir[2] = b[p]; rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; rsum += rinsum; gsum += ginsum; bsum += binsum; stackpointer = (stackpointer + 1) % div; sir = stack[stackpointer]; routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; rinsum -= sir[0]; ginsum -= sir[1]; binsum -= sir[2]; yi += w; } } bitmap.setPixels(pix, 0, w, 0, 0, w, h); return bitmap; }其实也就是对bitmap进行了一定的像素处理。参数sentBitmap其实就是我们要处理的图片,radius就是传入的要处理达到的模糊度,canReuseInBitmap标识是否能够重用这个sentBitmap. 加载bitmap和渲染的问题我是采用上面的addOnPreDrawListener来处理的。对于处理listview中加载并且模糊的卡顿效果我采用的方法是对bitmap采取压缩之后再模糊处理 将处理之后的图片在通过bitmap的缩放,放大到我需要的指定大小。
public static void blur(Bitmap bkg, TextView textview, ImageView view,Context context) {int radius = 2;float scaleFactor = 8;Bitmap overlay = Bitmap.createBitmap((int) (textview.getMeasuredWidth() / scaleFactor),(int) (textview.getMeasuredHeight() / scaleFactor),Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(overlay);Paint paint = new Paint();paint.setFlags(Paint.FILTER_BITMAP_FLAG);canvas.translate(-textview.getLeft() / scaleFactor, -textview.getTop()/ scaleFactor);canvas.scale(1 / scaleFactor, 1 / scaleFactor);canvas.drawBitmap(bkg, 0, 0, paint);//overlay = doBlur(overlay, radius, true);overlay = blurBitmap(overlay, context);view.setBackground(new BitmapDrawable(context.getResources(), zoomImg(overlay, textview.getMeasuredWidth(),textview.getMeasuredHeight())));}/** * 处理图片 * * @param bm * 所要转换的bitmap * @param newWidth新的宽 * @param newHeight新的高 * @return 指定宽高的bitmap */public static Bitmap zoomImg(Bitmap bm, int newWidth, int newHeight) {// 获得图片的宽高int width = bm.getWidth();int height = bm.getHeight();// 计算缩放比例float scaleWidth = ((float) newWidth) / width;float scaleHeight = ((float) newHeight) / height;// 取得想要缩放的matrix参数Matrix matrix = new Matrix();matrix.postScale(scaleWidth, scaleHeight);// 得到新的图片Bitmap newbm = Bitmap.createBitmap(bm, 0, 0, width, height, matrix,true);return newbm;}
函数blur通过我们需要模糊的控件(此处为TextView)产生的bitmap结合textView的宽和高创建一个缩小了scaleFactor倍大小的已经有模糊效果的bitmap对象,最后再通过zoomImg方法放大scaleFactor倍数(这里的通用性可能不好,应该将scaleFactor写成输入参数)放到我们定义好的以及悬浮在TextView(需要模糊效果)的上面的wrapContent大小的ImageView里面,用setBackGround放大设置位背景,这样就覆盖住了TextView。由于开始ImageView没有任何内容和背景颜色 ,所有他并不现实出来,之后设置背景之后才会现实出来了。下面是效果的对比。传送门
- Android之高斯模糊的记录
- Android 高斯模糊一些细节记录
- 高斯模糊学习记录
- android 高斯模糊
- android高斯模糊
- Android高斯模糊
- android高斯模糊
- Android 高斯模糊
- 高斯模糊Android
- Android高斯模糊
- android图片高斯模糊的方法
- android 图片的高斯模糊实现
- Android开发 高斯模糊的实现
- Android实现图片的高斯模糊
- android使用方法---图片的高斯模糊
- android高斯模糊的简单实现
- android背景高斯模糊的Dialog
- android 图片高斯模糊
- 堆栈区别和堆栈溢出原因
- 批处理统计文件大小
- Supporting Multiple Screens(支持Android各种屏幕尺寸)
- A building includes angularjs and jQuery!
- 各大排序算法性能比较
- Android之高斯模糊的记录
- Photon服务器入门教程二
- CC2640的RTOS系统代码的那点事
- ODOO 新API修饰符
- 微信开通状态检测助手如何进行批量加粉
- 使程序在Linux下后台运行 (关掉终端继续让程序运行的方法)
- 其实你不知道MultiDex到底有多坑
- css5_浮动
- LeakCanary开源项目 内存泄漏检测工具 库 使用