先贴个找的:图片处理——使用NDK添加文字和图片水印 作者:徐福记456

直接复制到Android Studio中,跑一下,正常。
但是需求不一样,得改。

需求效果如下

android 开发 给全局增加水印功能 android图片水印_数组

  1. 先拍一个照片①
  2. 指定文字生成半透明图片②
  3. 指定信息生成二维码③
  4. ②和③拼接成④
  5. ④再印在①上

原功能的拼接是上下拼接,得改

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);
  • ……