在 Android NDK 中,我们可以使用 jnigraphics 来更高效地操作 Bitmap。jnigraphics 库会公开允许访问 Java Bitmap 对象的像素缓冲区的 API。工作流如下:
- 调用
AndroidBitmap_getInfo()以检索信息,例如指定位图句柄的宽度和高度。 - 调用
AndroidBitmap_lockPixels()以锁定像素缓冲区并检索指向它的指针。这样做可确保像素在应用调用AndroidBitmap_unlockPixels()之前不会移动。 - 对像素缓冲区进行相应修改,以使其符合相应像素格式、宽度和其他特性。
- 调用
AndroidBitmap_unlockPixels()以解锁缓冲区。
这里以对图片二值化为例,代码示例如下:
#include <android/bitmap.h>
// 使用extern "C" 来确保该函数在C++编译环境中使用C语言的命名和调用约定,因为JNI(Java Native Interface)要求使用C语言的命名规则
// JNIEXPORT 和 JNICALL 是JNI的宏,用于声明导出的JNI函数
// Java_com_example_bitmapbinary_BitmapBinaryUtils_applyBitmapBinary 是JNI函数的标准命名,对应Java类 com.example.bitmapbinary.BitmapBinaryUtils 中的 applyBitmapBinary 方法
extern "C" JNIEXPORT void JNICALL Java_com_example_bitmapbinary_BitmapBinaryUtils_applyBitmapBinary
(JNIEnv *env, jobject utils, jobject jBitmap) {
int result;
// 定义一个AndroidBitmapInfo结构体变量sourceInfo,用于存储源Bitmap的相关信息,如宽度、高度、像素格式等
AndroidBitmapInfo sourceInfo;
// 调用AndroidBitmap_getInfo函数获取源Bitmap的信息,并将结果存储在sourceInfo中
// env 是JNI环境指针,jBitmap 是Java层传递过来的Bitmap对象,&sourceInfo 是存储信息的结构体指针
result = AndroidBitmap_getInfo(env, jBitmap, &sourceInfo);
// 如果获取信息失败,result会小于0,此时直接返回,不进行后续操作
if (result < 0) {
return;
}
// 定义一个指向uint32_t类型的指针sourceData,用于存储源Bitmap的像素数据
// uint32_t是32位无符号整数,这里每个像素用32位表示,对应ARGB格式,每个通道占8位
uint32_t* sourceData;
// 调用AndroidBitmap_lockPixels函数锁定源Bitmap的像素地址
// 锁定像素地址是为了确保在操作像素数据时,地址不会发生改变
// (void**)& sourceData 是将sourceData指针转换为void**类型,以满足函数参数要求
result = AndroidBitmap_lockPixels(env, jBitmap, (void**)& sourceData);
// 如果锁定像素地址失败,result会小于0,此时直接返回,不进行后续操作
if (result < 0) {
return;
}
// 定义变量color用于存储当前像素的颜色值
int color;
// 定义变量red、green、blue、alpha分别用于存储当前像素的红、绿、蓝、透明度通道的值
int red, green, blue , alpha;
// 从sourceInfo中获取源Bitmap的宽度和高度
int width = sourceInfo.width;
int height = sourceInfo.height;
// 定义变量w和h用于循环遍历Bitmap的像素点
int w, h;
// 外层循环遍历Bitmap的每一行
for (h = 0; h < height; h++) {
// 内层循环遍历Bitmap的每一列
for (w = 0; w < width; w++) {
// 根据当前像素的行和列计算其在像素数组中的索引,并获取该像素的颜色值
color = sourceData[h * width + w];
// 通过位运算提取当前像素的透明度通道值
// 0xff000000 是一个32位的掩码,用于提取最高8位的透明度信息
alpha = color & 0xff000000;
// 通过位运算提取当前像素的红色通道值,并右移16位,将其转换为0-255的范围
// 0x00ff0000 是一个32位的掩码,用于提取红色通道信息
red = (color & 0x00ff0000) >> 16;
// 通过位运算提取当前像素的绿色通道值,并右移8位,将其转换为0-255的范围
// 0x0000ff00 是一个32位的掩码,用于提取绿色通道信息
green = (color & 0x0000ff00) >> 8;
// 通过位运算提取当前像素的蓝色通道值
// 0x000000ff 是一个32位的掩码,用于提取蓝色通道信息
blue = color & 0x000000ff;
// 使用加权平均算法计算当前像素的最佳灰度值
// 0.299、0.587、0.114 是根据人眼对不同颜色的敏感度分配的权重
color = red * 0.299 + green * 0.587 + 0.114 * blue;
// 如果计算得到的灰度值小于等于95,则将其设置为0(黑色)
if (color <= 95) {
color = 0;
}
// 否则将其设置为255(白色)
else
{
color = 255;
}
// 将处理后的颜色值重新组合成一个32位的ARGB值,并存储回像素数组中
// alpha 保持不变,red、green、blue通道都设置为处理后的颜色值
sourceData[h * width + w] = alpha | (color << 16) | (color << 8) | color;
}
}
// 调用AndroidBitmap_unlockPixels函数解锁源Bitmap的像素地址
// 解锁后,系统可以继续管理和使用这些像素数据
AndroidBitmap_unlockPixels(env, jBitmap);
}
效果如下图所示:
更多关于的 jnigraphics 库的方法,可以见Bitmap | Android NDK | Android Developers