最近想练习一下NDK,基于我接下来的目标是多媒体,多以想多点学习一下关于滤镜的知识,黑白滤镜是最简单的了。
这里实现的是将一张彩色图片转成黑白图片,是黑白滤镜的基础!
直接上码:
// java 代码
public void gray(View view) {
Bitmap source = BitmapFactory.decodeResource(getResources(), R.drawable.time);
Bitmap target = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
BitmapTools.gray(source, target);
targetView.setImageBitmap(target);
}
// c 代码
#include <jni.h>
#include <android/bitmap.h>
#include <android/log.h>
#define TAG "bitmap-lib"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG ,__VA_ARGS__)
extern "C"
JNIEXPORT void JNICALL
Java_com_johan_ndk_BitmapTools_gray(JNIEnv *env, jobject obj, jobject source, jobject target) {
int result;
// 获取源Bitmap相关信息:宽、高等
AndroidBitmapInfo sourceInfo;
result = AndroidBitmap_getInfo(env, source, &sourceInfo);
if (result < 0) {
LOGE("get bitmap info error : %d", result);
return;
}
// 获取源Bitmap像素数据 这里用的是32位的int类型 argb每个8位
uint32_t *sourceData;
result = AndroidBitmap_lockPixels(env, source, (void**) &sourceData);
if (result < 0) {
LOGE("bitmap lock pixels error : %d", result);
return;
}
// 锁定Bitmap
// 并获取目标Bitmap像素数据
// 注意:传进来的Bitmap只是一张空的Bitmap
uint32_t *targetData;
result = AndroidBitmap_lockPixels(env, target, (void**) &targetData);
if (result < 0) {
LOGE("bitmap lock pixels error : %d", result);
return;
}
// 遍历各个像素点
int color;
int alpha = 0xff << 24;
int red, green, blue;
int width = sourceInfo.width;
int height = sourceInfo.height;
int w, h;
for (h = 0; h < height; h++) {
for (w = 0; w < width; w++) {
color = sourceData[h*width+w];
red = (color & 0x00ff0000) >> 16;
green = (color & 0x0000ff00) >> 8;
blue = color & 0x000000ff;
// rgb颜色相同就是黑白图片了 取平均值只是一个方案
color = (red + green + blue) / 3;
targetData[h*width+w] = alpha | (color << 16) | (color << 8) | color;
}
}
AndroidBitmap_unlockPixels(env, source);
AndroidBitmap_unlockPixels(env, target);
}
注意Cmake文件要添加库,否则会报找不到 AndroidBitmap_getInfo 等Bitmap方法:
target_link_libraries(
...
jnigraphics
)
效果:
以上是我仿 这篇博文 写的,但是我看到这篇博文有一条评论,说是像素点在c上存在大小端的问题,于是乎我改了一点代码:
...
for (h = 0; h < height; h++) {
for (w = 0; w < width; w++) {
color = sourceData[h*width+w];
red = (color & 0x00ff0000) >> 16;
green = (color & 0x0000ff00) >> 8;
blue = color & 0x000000ff;
blue += 60;
if (blue > 255) {
blue = 255;
}
color = (red + green + blue) / 3;
targetData[h*width+w] = alpha | (red << 16) | (green << 8) | blue;
}
}
...
这样改之后,界面应该偏冷色,结果却是:
居然是暖色调了,证明的确存在大小端的问题,所以在C层中,Bitmap像素点的值是BGR,而不是RGB,也就是说,高端到低端:B,G,R!所以代码得改一下:
...
for (h = 0; h < height; h++) {
for (w = 0; w < width; w++) {
color = sourceData[h*width+w];
blue = (color & 0x00ff0000) >> 16;
green = (color & 0x0000ff00) >> 8;
red = color & 0x000000ff;
blue += 80;
if (blue > 255) {
blue = 255;
}
color = (red + green + blue) / 3;
targetData[h*width+w] = alpha | (blue << 16) | (green << 8) | red;
}
}
...
这样改了之后,结果就正常为冷色调了:
至于黑白时没有高低端问题呢,是因为color去的是rgb的平均值,所以不论是r+g+b,还是b+g+r,值都是一样!!
通过这个例子,不仅复习了一下NDK操作,而且还知道了Bitmap像素点存在高低端问题!!值了~