IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android NDK之旅——图片高斯模糊,手握5家大厂offer -> 正文阅读

[移动开发]Android NDK之旅——图片高斯模糊,手握5家大厂offer

target_link_libraries(native-lib ${log-lib})


*   **cmake\_minimum\_required(VERSION 3.4.1)**  
    CMake最小版本使用的是3.4.1。
*   **add\_library()**  
    配置so库信息(为当前当前脚本文件添加库)
    *   **native-lib**  
        这个是声明引用so库的名称,在项目中,如果需要使用这个so文件,引用的名称就是这个。值得注意的是,实际上生成的so文件名称是libnative-lib。
    *   **SHARED**  
        这个参数表示共享so库文件,也就是在Run项目或者build项目时会在目录`intermediates\transforms\mergeJniLibs\debug\folders\2000\1f\main`下生成so文件。
    *   **src/main/cpp/native-lib.cpp**  
        构建so库的源文件。
*   **find\_library()**  
    查找一个库文件
    *   **log-lib**  
        这个指定的是在NDK库中每个类型的库会存放一个特定的位置,而log库存放在log-lib中
    *   **log**  
        指定使用log库
*   **target\_link\_libraries()**  
    如果你本地的库(native-lib)想要调用log库的方法,那么就需要配置这个属性,意思是把NDK库关联到本地库。
    *   **native-lib**  
        要被关联的库名称
    *   **${log-lib}**  
        要关联的库名称,要用大括号包裹,前面还要有$符号去引用。

### 4 了解JNI的C/C++规范

#### 数据类型

JNI的数据类型包含两种,分别是基本类型和引用类型,它们和Java中的数据类型对应关系如下两表所示。

| 基本数据类型 |
| :-: |
| JNI类型 | Java类型 | 描述 |
| :-: | :-: | :-: |
| jboolean | boolean | 无符号8位整型 |
| jbyte | byte | 无符号8位整型 |
| jchar | char | 无符号16位整型 |
| jshort | short | 有符号16位整型 |
| jint | int | 32位整型 |
| jlong | long | 64位整型 |
| jfloat | float | 32位浮点型 |
| jdouble | double | 64位浮点型 |
| void | void | 无类型 |

| 引用数据类型 |
| :-: |
| JNI类型 | Java类型 | 描述 |
| :-: | :-: | :-: |
| jobject | Object | Object类型 |
| jclass | Class | Class类型 |
| jstring | String | String类型 |
| jobjectArray | Object\[\] | 对象数组 |
| jbooleanArray | boolean\[\] | boolean数组 |
| jbyteArray | byte\[\] | byte数组 |
| jcharArray | char\[\] | char数组 |
| jshortArray | short\[\] | short数组 |
| jintArray | int\[\] | int数组 |
| jlongArray | long\[\] | long数组 |
| jfloatArray | float\[\] | float数组 |
| jdoubleArray | double\[\] | double数组 |
| jthrowable | Throwable | Throwable |

#### JNI的类型签名

JNI的类型签名标识了一个特定的Java类型,这个类型既可以是类也可以是方法,也可以是数据类型。

*   类的签名比较简单,它采用 L+包名+类型+; 的形式,只需要将其中的.替换为/即可。例如java.lang.String, 它的签名为Ljava/lang/String; ,注意末尾的;也是签名一部分。
*   基本数据类型的签名采用一系列大写字母来表示, 如下表所示

| 基本数据类型的签名 |
| :-: |
| Java类型 | 签名 | Java类型 | 签名 | Java类型 | 签名 |
| :-: | :-: | :-: | :-: | :-: | :-: |
| boolean | Z | byte | B | char | C |
| short | S | int | I | long | J |
| float | F | double | D | void | V |

#### JNI C/C++函数编写

先来看看Android Studio为我们生成的示例

JNIEXPORT jstring JNICALL
Java_com_glee_myapplication_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = “Hello from C++”;
return env->NewStringUTF(hello.c_str());
}


*   **JNIEXPORT & JNICALL**  
    JNIEXPORT和JNICALL这两个宏(被定义在jni.h)确保这个函数在本地库外可见,并且编译器会进行正确的调用转换。
*   **函数规范**  
    在JNI中C/C++的函数名是有规范要求的,由以下几部分串接而成
    *   `Java_前缀`
    *   `完全限定的类名,并用下划线“_”作为分隔符`
    *   `第一参数JNIEnv* env`
    *   `第二个参数jobject或jclass`
    *   `其他参数按类型映射`
    *   `返回参数按类型映射`

JNI层操作Bitmap对象
--------------

### 原理

Android中JNI层处理Bitmap通常有两种方法

*   获取到Bitmap中的byte数组并传入native方法,JNI层处理得到的byte数组后返回一个新的byte数组,Java层重建Bitmap对象。(不推荐)
*   Java层直接向JNI层传入Bitmap的引用,JNI层得到Bitmap对象的图像数据的地址,直接修改Bitmap的byte数组。

阅读了很多篇博客,很多开发者都会采用第一种方法,本人是极不推荐的。这种方法会在内存中重建一个byte数组,会造成内存的浪费,性能低下。  
第二种方法是性能最优的,JNI层充分利用的C/C++指针的特性,直接获取到Bitmap中byte数组在内存中的地址,通过指针直接修改图像数据,所以用到了NDK中的android/bitmap.h。

### android/bitmap.h

android/bitmap.h这个头文件用于在JNI层操作Bitmap对象的,其包含于jnigraphics库中,所以要在CMakeLists.txt中的target\_link\_libraries加入-ljnigraphics,如下

target_link_libraries(native-lib -ljnigraphics ${log-lib})


三个常用函数

*   AndroidBitmap\_getInfo() 从位图句柄获得信息(宽度、高度、像素格式)
*   AndroidBitmap\_lockPixels() 对像素缓存上锁,即获得该缓存的指针。
*   AndroidBitmap\_unlockPixels() 解锁

### JNI接口函数

请看注释

JNIEXPORT void JNICALL
Java_com_glee_ndkroad1006_MainActivity_gaussBlur(JNIEnv env, jobject / this /, jobject bmp) {
AndroidBitmapInfo info = {0};//初始化BitmapInfo结构体
int data=NULL;//初始化Bitmap图像数据指针
AndroidBitmap_getInfo(env, bmp, &info);
AndroidBitmap_lockPixels(env, bmp, (void *) &data);//锁定Bitmap,并且获得指针
/高斯模糊算法作对int数组进行处理
/
//调用gaussBlur函数,把图像数据指针、图片长宽和模糊半径传入
gaussBlur(data,info.width,info.height,80);
/
**************************************************/
AndroidBitmap_unlockPixels(env,bmp);//解锁
}


**这里用到的gaussBlur函数代码将在文章最后列出。**  
**这里用到的gaussBlur函数代码将在文章最后列出。**  
**这里用到的gaussBlur函数代码将在文章最后列出。**

### Java层代码

请看注释

public class MainActivity extends AppCompatActivity {

static {
    //通过静态代码块加载so库
    System.loadLibrary("native-lib");
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //初始化两个ImageView
    ImageView iv1 = (ImageView) findViewById(R.id.img1);
    ImageView iv2 = (ImageView) findViewById(R.id.img2);
    //iv1设置图片
    iv1.setImageResource(R.drawable.test);
    //生成bitmap对象
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
    //调用native方法,传入Bitmap对象,对Bitmap进行高斯迷糊处理
    gaussBlur(bitmap);
    //把Bitmap对象设置给iv2
    iv2.setImageBitmap(bitmap);
}
//native方法声明
public native void gaussBlur(Bitmap bitmap);

}


### 运行效果

上方的ImageView是没有进行高斯模糊处理的,下方的ImageView调用了JNI方法进行高斯模糊处理。

![](https://user-gold-cdn.xitu.io/2017/10/7/a0019ad6464478e1d68b49c44fec5755?imageView2/0/w/1280/h/960/ignore-error/1)

### 高斯模糊算法

void gaussBlur1(int* pix, int w, int h, int radius)
{
float sigma = (float) (1.0 * radius / 2.57);
float deno = (float) (1.0 / (sigma * sqrt(2.0 * PI)));
float nume = (float) (-1.0 / (2.0 * sigma * sigma));
float* gaussMatrix = (float*)malloc(sizeof(float)* (radius + radius + 1));
float gaussSum = 0.0;
for (int i = 0, x = -radius; x <= radius; ++x, ++i)
{
float g = (float) (deno * exp(1.0 * nume * x * x));
gaussMatrix[i] = g;
gaussSum += g;
}
int len = radius + radius + 1;
for (int i = 0; i < len; ++i)
gaussMatrix[i] /= gaussSum;
int* rowData = (int*)malloc(w * sizeof(int));
int* listData = (int*)malloc(h * sizeof(int));
for (int y = 0; y < h; ++y)
{
memcpy(rowData, pix + y * w, sizeof(int) * w);
for (int x = 0; x < w; ++x)
{
float r = 0, g = 0, b = 0;
gaussSum = 0;
for (int i = -radius; i <= radius; ++i)
{
int k = x + i;
if (0 <= k && k <= w)
{
//得到像素点的rgb值
int color = rowData[k];
int cr = (color & 0x00ff0000) >> 16;
int cg = (color & 0x0000ff00) >> 8;
int cb = (color & 0x000000ff);
r += cr * gaussMatrix[i + radius];
g += cg * gaussMatrix[i + radius];
b += cb * gaussMatrix[i + radius];
gaussSum += gaussMatrix[i + radius];
}
}
int cr = (int)(r / gaussSum);
int cg = (int)(g / gaussSum);
int cb = (int)(b / gaussSum);
pix[y * w + x] = cr << 16 | cg << 8 | cb | 0xff000000;
}

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-31 15:33:43  更:2021-08-31 15:34:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/31 5:50:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码