之前项目中,有个需求是对Activtiy背景进行虚化,实现毛玻璃效果。实现的大体思路:
- 对手机屏幕进行截屏处理,获得截屏图片的bitmap;
- 对bitmap进行相应的config处理和缩放像素处理;
- 通过高斯模糊算法对处理后的bitmap进行虚化;
- 将虚化后的bitmap作为背景图展示。
虽然实现了,但由于效果不佳或是影响性能,最终选择放弃。最近闲的没事,准备将这件事给完成。
因为之前对bitmap的算法处理是通过java代码实现的,如果想提高性能或是减少处理时间,达到秒开的效果,必须设置虚化的程度低,但这样效果并不好看,但只是一味追求效果,由于java代码运行比较慢,在处理时间上必定会有延迟,总之就是有性能没效果,有效果没性能的矛盾,体验十分不佳,网上也给出过方案是通过c的方式实现,今天就来完成,随便回顾一遍jni的流程,长时间不碰全忘...
配置NDK,生成.so动态库
- build.gradle文件下,添加ndk配置:
android {
defaultConfig {
minSdkVersion 21
targetSdkVersion 25
...
ndk {
moduleName "blur_lib" // 动态库名称
abiFilters "armeabi", "armeabi-v7a", "x86" // 相应的架构平台
ldLibs "log" //log输出
ldLibs "jnigraphics" //graphic相关jni
}
}
...
}
项目的gradle.properties添加支持NDKandroid.useDeprecatedNdk=true
local.properties添加ndk-bundle路径
ndk.dir=C\:\\Users\\Administrator\\AppData\\Local\\Android\\Sdk\\ndk-bundle
- 创建NativeHelper类如下:
public class NativeHelper {
static {
System.loadLibrary("blur_lib");
}
// 参数r为对bitmap虚化的程度范围
static native void blurBitmap(Object bitmap, int r);
}
在Terminal中cd到java目录下生成.h头文件,方便得到c中的类名,输入命令行:
javah -jni com.pecoo.blurjnidemo.NativeHelper
- main下创建一个jni folder,里面创建.c/c++和.h头文件,高斯算法代码粘进来,并在.h头文件进行相应的方法申明。
- 再创建一个c/c++文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_pecoo_blurjnidemo_NativeHelper */
#ifndef _Included_com_pecoo_blurjnidemo_NativeHelper
#define _Included_com_pecoo_blurjnidemo_NativeHelper
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_pecoo_blurjnidemo_NativeHelper
* Method: blurBitmap
* Signature: (Ljava/lang/Object;I)V
*/
#include <android/log.h>
#include <android/bitmap.h>
#include "stackblur.h" // 在第一步中创建的.h头文件,下面可以调用里面的方法
// log宏定义
#define TAG "Native_Blur_Jni"
#define LOG_D(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)
JNIEXPORT void JNICALL Java_com_pecoo_blurjnidemo_NativeHelper_blurBitmap
(JNIEnv *env, jclass obj, jobject bitmapIn, jint r)
{
AndroidBitmapInfo infoIn;
void *pixels;
// 获取bitmap的信息
if (AndroidBitmap_getInfo(env, bitmapIn, &infoIn) != ANDROID_BITMAP_RESULT_SUCCESS) {
LOG_D("AndroidBitmap_getInfo failed!");
return;
}
// 检测bitmap是不是这两种格式,因为算法中只有对这两种图片会做处理
if (infoIn.format != ANDROID_BITMAP_FORMAT_RGBA_8888 &&
infoIn.format != ANDROID_BITMAP_FORMAT_RGB_565) {
LOG_D("Only support ANDROID_BITMAP_FORMAT_RGBA_8888 and ANDROID_BITMAP_FORMAT_RGB_565");
return;
}
// 锁定图片
if (AndroidBitmap_lockPixels(env, bitmapIn, &pixels) != ANDROID_BITMAP_RESULT_SUCCESS) {
LOG_D("AndroidBitmap_lockPixels failed!");
return;
}
// 得到宽高
int h = infoIn.height;
int w = infoIn.width;
if (infoIn.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
// 调用stackblur.c中的blur_ARGB_8888()或blur_RGB_565()
pixels = blur_ARGB_8888((int *) pixels, w, h, r);
} else if (infoIn.format == ANDROID_BITMAP_FORMAT_RGB_565) {
pixels = blur_RGB_565((short *) pixels, w, h, r);
}
// 对应上面的AndroidBitmap_lockPixels()
AndroidBitmap_unlockPixels(env, bitmapIn);
}
#ifdef __cplusplus
}
#endif
#endif
5.生成.so动态库:Build->Rebuild Project完成后,会在build文件下生成相应平台的.so。复制到main下的jniLibs中
若失败试试
D:\workspace\ndk\NDKDemo\myapplication\src\main>javah -d jni -classpath C:\Users\Administrator\AppData\Local\Android\Sdk\platforms\android-20\android.jar;..\..\build\intermediates\classes\debug com.pecoo.myapplication.NativeUtils
虚化图片,实现效果:
对于截屏,获取到截屏的bitmap步骤这儿就忽略了,直接拿张资源图片进行处理,显示在界面上
// 获得bitmap
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.blur_img);
// 获得图片的宽高
int width = bitmap.getWidth();
int height = bitmap.getHeight();
// 设置想要的大小
Display display = getWindowManager().getDefaultDisplay();
int newWidth = display.getWidth();
int newHeight = display.getHeight();
// 计算缩放比例
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Log.d(TAG, "scaleWidth:" + scaleWidth);
Log.d(TAG, "scaleHeight:" + scaleHeight);
// 取得想要缩放的matrix参数
Matrix matrix = new Matrix();
// matrix.postScale(scaleWidth, scaleHeight);
// 实现模糊效果之前,这里可对bitmap进行更大缩放,减少像素点还可提高性能
float scaleFactor = 10;
float scale = 1f / scaleFactor;
matrix.postScale(scale, scale);
// 得到新的图片
Bitmap newbm = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix,true);
bitmap.recycle();
子线程中调用处理bitmap的本地函数
private void blur(final Bitmap bitmap, final int radius) {
Thread thread = new Thread() {
@Override
public void run() {
super.run();
// blurNatively()主要是检查图片是否为ARGB8888和RGB565,如果是则调用 NativeHelper中的本地方法blurBitmap()
final Bitmap ret = blurNatively(bitmap, radius,true);
runOnUiThread(new Runnable() {
@Override
public void run() {
mImageView.setBackground(new BitmapDrawable(getResources(), ret));
}
});
}
};
thread.start();
}
最后把 github地址发一下,仅供参考。