Android JNI原生C++修改Bitmap红通道像素值异常求助
问题根因排查
- 通道字节顺序错误
Android的ANDROID_BITMAP_FORMAT_RGBA_8888格式的像素在native内存中为小端存储,32位无符号整数的位分配为:
- 0~7位:蓝色通道(B)
- 8~15位:绿色通道(G)
- 16~23位:红色通道(R)
- 24~31位:透明度通道(A)
你原代码把红色值写入0~7位,相当于给蓝色通道赋值255,所以最终输出全蓝色图像,同时未对Alpha通道赋值,导致透明度异常,出现全透明/全白的问题。
- 通道字节顺序错误
- 新Bitmap宽高传参错误
调用createBitmap时你把原Bitmap的height作为宽度、width作为高度传入,导致新画布尺寸不对。
- 新Bitmap宽高传参错误
- 像素翻转逻辑无必要且错误
你未说明需要翻转图像,原有的逆向x轴遍历逻辑会导致图像横向翻转,不符合常规需求。
- 像素翻转逻辑无必要且错误
修复后完整实现代码
#include <android/bitmap.h> #include <algorithm> // 用于通道值溢出保护 jobject Java_com_journaldev_androidjnibasics_MainActivity_sendMyBitmap(JNIEnv *env, jobject thiz, jobject bitmap) { AndroidBitmapInfo info; int ret; if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) { return NULL; } if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) { return NULL; } void *bitmapPixels; if ((ret = AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels)) < 0) { return NULL; } uint32_t *src = (uint32_t *) bitmapPixels; int pixelsCount = info.height * info.width; uint32_t *tempPixels = new uint32_t[pixelsCount]; for (int y=0; y<info.height; y++) { uint32_t * line = (uint32_t *)bitmapPixels; for (int x=0; x<info.width; x++) { // 修正通道读取顺序 uint8_t blue = (uint8_t) (line[x] & 0x000000FF); uint8_t green = (uint8_t)((line[x] & 0x0000FF00) >> 8); uint8_t red = (uint8_t) ((line[x] & 0x00FF0000) >> 16); uint8_t alpha = (uint8_t) ((line[x] & 0xFF000000) >> 24); // 测试:全红设置,若要实现红色通道提升可替换为 red = std::min(255, red + 偏移值); red = 255; green = 0; blue = 0; alpha = 255; // 明确设置为不透明,避免透明异常 // 修正通道写入顺序 line[x] = (alpha << 24) | (red << 16) | (green << 8) | blue; } bitmapPixels = (char *)bitmapPixels + info.stride; } memcpy(tempPixels, src, sizeof(uint32_t) * pixelsCount); AndroidBitmap_unlockPixels(env, bitmap); // 回收原Bitmap jclass bitmapCls = env->GetObjectClass(bitmap); jmethodID recycleFunction = env->GetMethodID(bitmapCls, "recycle", "()V"); if (recycleFunction == 0) { delete[] tempPixels; return NULL; } env->CallVoidMethod(bitmap, recycleFunction); // 修正新Bitmap宽高传参顺序 jmethodID createBitmapFunction = env->GetStaticMethodID(bitmapCls, "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;"); jstring configName = env->NewStringUTF("ARGB_8888"); jclass bitmapConfigClass = env->FindClass("android/graphics/Bitmap$Config"); jmethodID valueOfBitmapConfigFunction = env->GetStaticMethodID(bitmapConfigClass, "valueOf", "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;"); jobject bitmapConfig = env->CallStaticObjectMethod(bitmapConfigClass, valueOfBitmapConfigFunction, configName); jobject newBitmap = env->CallStaticObjectMethod(bitmapCls, createBitmapFunction, info.width, info.height, bitmapConfig); // 写入新Bitmap,移除不必要的翻转逻辑,直接拷贝即可 if ((ret = AndroidBitmap_lockPixels(env, newBitmap, &bitmapPixels)) < 0) { delete[] tempPixels; return NULL; } uint32_t *newBitmapPixels = (uint32_t *) bitmapPixels; memcpy(newBitmapPixels, tempPixels, sizeof(uint32_t) * pixelsCount); AndroidBitmap_unlockPixels(env, newBitmap); delete[] tempPixels; return newBitmap; }
内容的提问来源于stack exchange,提问作者mariya sebastian
相关产品推荐
相关产品推荐

