先贴个找的:图片处理——使用NDK添加文字和图片水印 作者:徐福记456
直接复制到Android Studio中,跑一下,正常。
但是需求不一样,得改。
需求效果如下
- 先拍一个照片①
- 指定文字生成半透明图片②
- 指定信息生成二维码③
- ②和③拼接成④
- ④再印在①上
原功能的拼接是上下拼接,得改
extern "C"
JNIEXPORT jintArray JNICALL
Java_com_xxx_xxx_pic_WaterMake_jointPicture(JNIEnv *env, jclass clazz,jintArray pixels, jint width, jint height,jintArray add_pixel, jint add_width,jint add_height) {
//像素数组拷贝到native层
jint *pixel = env->GetIntArrayElements(pixels, JNI_FALSE);
jint *addPixel = env->GetIntArrayElements(add_pixel, JNI_FALSE);
//这里假定图片宽度相同
// 并不是,其实是高度相同
//int size = width * (height+add_height);
int size = (width+add_width) * height;
jint *newPixel = new jint[size];
int x, y;
// 修改这里
for(x = 0 ; x < height ;x ++){
for ( y = 0; y < width; y ++) {
newPixel[x*(width + add_width) + y] = pixel[x*width + y];
}
}
for (x = 0; x < height; x ++) {
for (y = 0; y < add_width; ++y) {
newPixel[x*(width + add_width)+ y + width] = addPixel[x*add_width + y];
}
}
jintArray newPixels = env->NewIntArray(size);
env->SetIntArrayRegion(newPixels, 0, size, newPixel);
env->ReleaseIntArrayElements(pixels, pixel, 0);
env->ReleaseIntArrayElements(add_pixel, addPixel, 0);
return newPixels;
}
copy来的代码,也改一下
/**
* 使用NDK拼接图片
* @param bitmap bitmap
* @param addBitmap addBitmap
*/
public static Bitmap ndkJointPicture(Bitmap bitmap, Bitmap addBitmap){
if (bitmap == null || addBitmap == null){
return null;
}
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int[] pixels = new int[width * height];
//获取bitmap的所有pixel像素点
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
int addWidth = addBitmap.getWidth();
int addHeight = addBitmap.getHeight();
int[] addPixel = new int[addWidth * addHeight];
addBitmap.getPixels(addPixel, 0, addWidth, 0, 0, addWidth, addHeight);
//ndk处理像素点数组
int[] newPixels = jointPicture(pixels, width, height, addPixel, addWidth, addHeight);
// 使用 Bitmap.Config.ARGB_8888 模式 更改为需要的尺寸
Bitmap ndkBitmap = Bitmap.createBitmap(width + addWidth, height, Bitmap.Config.ARGB_8888);
//重新设置bitmap的所有pixel像素点
ndkBitmap.setPixels(newPixels, 0, width + addWidth, 0, 0, width + addWidth, height);
return ndkBitmap;
}
然后是水印,原功能是直接将图片印上去,替换掉原来的像素,单这个不符合需求,需要保留④的部分是透明的,需要保留①的信息,动手改一下。
/**
* 图片水印
*/
extern "C"
JNIEXPORT jintArray JNICALL
Java_com_xxx_xxx_pic_WaterMake_picWaterMark(JNIEnv *env, jclass clazz,jintArray pixels, jint width, jint height,jintArray overlay_pixels,jint overlay_width, jint overlay_height,jint offset_x, jint offset_y) {
// TODO: implement picWaterMark()
//像素数组拷贝到native层
jint *pixel = env->GetIntArrayElements(pixels, JNI_FALSE);
jint *overlayPixel = env->GetIntArrayElements(overlay_pixels, JNI_FALSE);
int size = width * height;
int alpha;
int red;
int green ;
int blue;
// 添加了变量
int alphaadd ;
int redadd ;
int greenadd;
int blueadd ;
int x, y;
for(x=0; x<width; x++){
for(y=0; y< height; y++){
//获取每一个像素点
int color = pixel[y*width + x];
alpha = (color >> 24) & 0xFF;
red = (color >> 16) & 0xFF;
green = (color >> 8) & 0xFF;
blue = color & 0xFF;
//获取覆盖层图片的像素点
if(x > offset_x && x < width && y > offset_y && y < height){
int addColor = overlayPixel[(y-offset_y)*overlay_width + (x-offset_x)];
//混合像素的RGBA
alphaadd = (addColor >> 24) & 0xFF ;
redadd = (addColor >> 16) & 0xFF ;
greenadd = (addColor >> 8) & 0xFF ;
blueadd = (addColor ) & 0xFF ;
// 在这里进行计算
red = (redadd * alphaadd +(red * (255-alphaadd)))/255;
green = (greenadd * alphaadd +(green * (255-alphaadd)))/255;
blue = (blueadd * alphaadd +(blue * (255-alphaadd)))/255;
}
//重新赋值给每个像素点
pixel[y*width + x] = (alpha << 24) | (red << 16) | (green << 8) | blue ;
}
}
jintArray newPixel = env->NewIntArray(size);
env->SetIntArrayRegion(newPixel, 0, size, pixel);
env->ReleaseIntArrayElements(pixels, pixel, 0);
env->ReleaseIntArrayElements(overlay_pixels, overlayPixel, 0);
return newPixel;
}
更改的主要点在这里:
red = (redadd * alphaadd +(red * (255-alphaadd)))/255;
green = (greenadd * alphaadd +(green * (255-alphaadd)))/255;
blue = (blueadd * alphaadd +(blue * (255-alphaadd)))/255;
根据获取到的alphaadd(④的透明度)计算叠加后的像素值。
举个栗子:
④的红色通道值为240,透明度为200;①的红色通道值为100;
那算一下,最后的值为:
(240x200 + 100x(255-200))/255≈209.8
取整:209,这就是像素叠加后的红色通道值。
印上去的java代码
public static Bitmap picWaterMarks(Bitmap bitmap, Bitmap addBitmap){
if (bitmap == null || addBitmap == null){
return null;
}
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int[] pixels = new int[width * height];
//获取bitmap的所有pixel像素点
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
int addWidth = addBitmap.getWidth();
int addHeight = addBitmap.getHeight();
int[] addPixel = new int[addWidth * addHeight];
addBitmap.getPixels(addPixel, 0, addWidth, 0, 0, addWidth, addHeight);
//ndk处理像素点数组
int[] newPixels = picWaterMark(pixels, width, height, addPixel, addWidth, addHeight,0,height-addHeight);
Bitmap ndkBitmap = Bitmap.createBitmap(width , height, Bitmap.Config.ARGB_8888);
//重新设置bitmap的所有pixel像素点
ndkBitmap.setPixels(newPixels, 0, width , 0, 0, width , height);
return ndkBitmap;
}
再来看看这个native方法的参数:
int[] newPixels = picWaterMark(pixels, width, height, addPixel, addWidth, addHeight,0,height-addHeight);
- pixels 底图的像素组
- width 底图的宽
- height 底图的高
- addPixel 水印图片的像素组
- addWidth 水印图片的宽
- addHeight 水印图片的高
- 0 水印x坐标起点
- height-addHeight 水印y坐标起点
需要注意的是,图像的坐标和数学坐标有差异:
- 左上角为(0,0)
- 左下角为(0,hight)
- 右上角为(width,0)
- 右下角为(width,hight)
取像素的顺序是横向的,即:
- pixels [0]=(0,0);
- pixels [1]=(1,0);
- pixels [2]=(2,0);
- ……