您的位置:首页 > 移动开发 > Android开发

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

2016-12-19 11:35 387 查看
版权声明:本文为博主原创,转载务必注明出处,谢谢!

目录(?)[+]
一效果演示
二适用RenderScript实现高斯模糊
三处理API向下兼容问题及注意点
四附

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


Hello,好久不见,好久没写博客了,总结了下原因有三,第一,变懒了;第二,变懒了;第三,还是变懒了;因为任何不更新博客的理由都是借口!


一、效果演示

项目中用到了高斯模糊效果,查阅过一些资料,考虑到性能问题最终还是选择使用Android自带的RenderScript库来实现,关于使用RenderScript来实现高斯模糊网上也有很多类似的方法,大部分都总结的比较乱,此处算是做一个整理吧,供有类似需求的同学参考及学习。




 
       


(项目效果图)
简单描述项目效果图的实现思路:
① 加载定义的xml的Layout
② 使用截屏方法获取当前窗口的Bitmap对象

③ 将Bitmap对象进行压缩及高斯模糊处理

④ 将处理过的模糊图对象作为①中所加载出来的Layout的Background
⑤ 将已经加入了模糊图对象的Layout添加到PopuWindow中并处理子条目的弹出方式

二、适用RenderScript实现高斯模糊

实现高斯模糊效果的方法有很多,可以用Java来实现,可以使用NDK来实现,也可以使用本文推荐的方式来实现(也是使用了JNI的方式),至于为什么选择使用RenderScript方式来实现,必然有一定优点。

优点:RenderScript方式,速度极快,约为java方式100倍的速度,NDK方式20倍速度(不同图片质量测试所得结果不同,供参考)

缺点:API17以上有效。(但Google已提供向下兼容的方法,文章后面会有介绍)

下面是使用RenderScript方式的核心代码:

[java]
view plain
copy

print?





/************************ 
* 高斯模糊处理 
* @param bitmap 
* @param context 
* @return 
***********************/  
  
public static Bitmap blurBitmap(Bitmap bitmap, Context context) {  
  
        // 用需要创建高斯模糊bitmap创建一个空的bitmap  
        Bitmap outBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);  
  
        // 初始化Renderscript,该类提供了RenderScript context,创建其他RS类之前必须先创建这个类,其控制RenderScript的初始化,资源管理及释放  
        RenderScript rs = RenderScript.create(context);  
  
        // 创建高斯模糊对象  
        ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));  
  
        // 创建Allocations,此类是将数据传递给RenderScript内核的主要方法,并制定一个后备类型存储给定类型  
        Allocation allIn = Allocation.createFromBitmap(rs, bitmap);  
        Allocation allOut = Allocation.createFromBitmap(rs, outBitmap);  
  
        //设定模糊度(注:Radius最大只能设置25.f)  
        blurScript.setRadius(15.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;  
    }  



/************************
* 高斯模糊处理
* @param bitmap
* @param context
* @return
***********************/

public static Bitmap blurBitmap(Bitmap bitmap, Context context) {

// 用需要创建高斯模糊bitmap创建一个空的bitmap
Bitmap outBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);

// 初始化Renderscript,该类提供了RenderScript context,创建其他RS类之前必须先创建这个类,其控制RenderScript的初始化,资源管理及释放
RenderScript rs = RenderScript.create(context);

// 创建高斯模糊对象
ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));

// 创建Allocations,此类是将数据传递给RenderScript内核的主要方法,并制定一个后备类型存储给定类型
Allocation allIn = Allocation.createFromBitmap(rs, bitmap);
Allocation allOut = Allocation.createFromBitmap(rs, outBitmap);

//设定模糊度(注:Radius最大只能设置25.f)
blurScript.setRadius(15.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;
}


该方法中注释描述的很清楚,但需要注意的是blurScript.setRadius();方法,该方法设定模糊度时Radius最大只能设置25.f,可能是对图片直接进行处理会导致模糊效果不好,故此值最大有效设定为25,但若想实现更深度的模糊效果,可以先压缩图片,降低图片的质量来实现更模糊的效果。

下方是图片的压缩处理方法:

[java]
view plain
copy

print?





/** 
     * Compress image by pixel, this will modify image width/height. 
     * 
     * @param imgPath image path 
     * @param pixelW target pixel of width 
     * @param pixelH target pixel of height 
     * @return 
     */  
    public static Bitmap ratio(String imgPath, float pixelW, float pixelH) {  
        BitmapFactory.Options newOpts = new BitmapFactory.Options();  
        // 开始读入图片,此时把options.inJustDecodeBounds 设回true,即只读边不读内容  
        newOpts.inJustDecodeBounds = true;  
        newOpts.inPreferredConfig = Config.RGB_565;  
        // Get bitmap info, but notice that bitmap is null now  
        Bitmap bitmap = BitmapFactory.decodeFile(imgPath,newOpts);  
  
        newOpts.inJustDecodeBounds = false;  
        int w = newOpts.outWidth;  
        int h = newOpts.outHeight;  
  
        float ww = pixelW;    //设置宽度为120f,可以明显看到图片缩小了  
        float hh = pixelH;    //设置高度为240f时,可以明显看到图片缩小了  
  
        //缩放比,由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可  
        int be = 1;//表示不缩放  
        if (w > h && w > ww) {          //如果宽度大的话根据宽度固定大小缩放  
            be = (int) (newOpts.outWidth / ww);  
        } else if (w < h && h > hh) {   //如果高度高的话根据宽度固定大小缩放  
            be = (int) (newOpts.outHeight / hh);  
        }  
        if (be <= 0) be = 1;  
        newOpts.inSampleSize = be;//设置缩放比例  
        // 开始压缩图片,注意此时已经把options.inJustDecodeBounds 设回false了  
        bitmap = BitmapFactory.decodeFile(imgPath, newOpts);  
        // 压缩好比例大小后再进行质量压缩  
        //return compress(bitmap, maxSize); //这里再进行质量压缩的意义不大,反而耗资源,删除  
        return bitmap;  
    }  



/**
* Compress image by pixel, this will modify image width/height.
*
* @param imgPath image path
* @param pixelW target pixel of width
* @param pixelH target pixel of height
* @return
*/
public static Bitmap ratio(String imgPath, float pixelW, float pixelH) {
BitmapFactory.Options newOpts = new BitmapFactory.Options();
// 开始读入图片,此时把options.inJustDecodeBounds 设回true,即只读边不读内容
newOpts.inJustDecodeBounds = true;
newOpts.inPreferredConfig = Config.RGB_565;
// Get bitmap info, but notice that bitmap is null now
Bitmap bitmap = BitmapFactory.decodeFile(imgPath,newOpts);

newOpts.inJustDecodeBounds = false;
int w = newOpts.outWidth;
int h = newOpts.outHeight;

float ww = pixelW;    //设置宽度为120f,可以明显看到图片缩小了
float hh = pixelH;    //设置高度为240f时,可以明显看到图片缩小了

//缩放比,由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
int be = 1;//表示不缩放
if (w > h && w > ww) {          //如果宽度大的话根据宽度固定大小缩放
be = (int) (newOpts.outWidth / ww);
} else if (w < h && h > hh) {   //如果高度高的话根据宽度固定大小缩放
be = (int) (newOpts.outHeight / hh);
}
if (be <= 0) be = 1;
newOpts.inSampleSize = be;//设置缩放比例
// 开始压缩图片,注意此时已经把options.inJustDecodeBounds 设回false了
bitmap = BitmapFactory.decodeFile(imgPath, newOpts);
// 压缩好比例大小后再进行质量压缩
//return compress(bitmap, maxSize); //这里再进行质量压缩的意义不大,反而耗资源,删除
return bitmap;
}
以上方法是使用RenderScript来实现高斯模糊的核心代码块及需注意的地方。但是,还是需要注意兼容问题,上述有提到该方法仅适用于API17以上才有效,那么问题来了,需要处理API向下兼容问题。

三、处理API向下兼容问题及注意点

在按照上述方法实现高斯模糊后,运行一看效果,满满的成就感,此时,Boss正好带着客户过来,小伙子,来,帮客户安装一个最新的版本(客户手机系统版本为Android4.0),装完后一点。。。。。。。。。。。这特么就尴尬了…………!

追踪bug时有同学可能会出现如下错误信息:

异常信息一:

09-21 15:07:34.417: E/AndroidRuntime(4476): android.support.v8.renderscript.RSRuntimeException: Error loading RS jni library: java.lang.UnsatisfiedLinkError: Couldn't load RSSupport: findLibrary returned null………………

异常信息二:

java.lang.NoClassDefFoundError: android.renderscript.ScriptIntrinsicBlur………………

解决方案:

报错信息为android.support.v8.renderscript.RSRuntimeException: Error loading 以及 java.lang.UnsatisfiedLinkError: Couldn't load RSSupport
from loader dalvik.system.PathClassLoader 是由于在4.2以上的手机自带 librsjni.so和libRSSupport.so库,而4.2以下某些手机没有这两个jni库。所以得把这两个jni 导入到我们的工程下便可。那么文件在何处呢?

以下是本人的文件路径:C:\Tools\android-sdk\build-tools\23.0.3\renderscript\lib\packaged,renderscript-v8.jar包位于renderscript\lib目录下。

即:android sdk 路径下 build-tools\各个版本\renderscript\lib\packaged 下的四个目录,这里需要注意jar包和.so版本的选择最好选最新的,例如24.0.0中增加了x86-64的适配。



好了,到这里就可以完美的兼容4.2以下的版本了。另外,还有一个最最最重要的注意点,我被这个细节困扰了至少2小时,现在想起来还觉得蛋疼,在你做完上面的一切事情之后,请注意一定能要将import的包路径更换过来:import android.renderscript更换为import android.support.v8.renderscript。具体如下:

[java]
view plain
copy

print?





import android.support.v8.renderscript.Allocation;  
import android.support.v8.renderscript.Element;  
import android.support.v8.renderscript.RenderScript;  
import android.support.v8.renderscript.ScriptIntrinsicBlur;  



import android.support.v8.renderscript.Allocation;
import android.support.v8.renderscript.Element;
import android.support.v8.renderscript.RenderScript;
import android.support.v8.renderscript.ScriptIntrinsicBlur;

最最最最后需要注意的一点,若项目中对代码加了混淆,别忘了这个。OK,就到这里了。

四、附:

另外分享两个处理高斯模糊的GitHub:

https://github.com/wl9739/BlurredView

https://github.com/robinxdroid/Blur

【转载注明gao_chun的Blog:http://blog.csdn.NET/gao_chun/article/details/52193511】
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: