图片处理——使用NDK添加文字和图片水印

        马化腾说过腾讯的服务器一天存储几个亿的图片,可见图片已经成为移动社交必不可少的元素。而用户往往喜欢在原图基础上编辑文字或者添加配图,达到图文并茂、生动形象的目的,以表达此时此地此景此物。添加水印已成为美图秀秀、QQ天天P图、各大直播平台的必备技能。接下来给大家介绍下,这是基于android移动端的NDK处理图片,添加文字或者图片水印。

        先看下添加水印后的效果:


        一张图片是由宽x高的二维矩阵像素点构成的,添加水印就是将覆盖层的矩阵像素与原图矩阵像素叠加。而一个像素是由ARGB(Alpha、Red、Green、Blue)组成,像素叠加需要拆分成ARGB分量进行叠加。native层处理代码如下:

jintArray
Java_com_frank_image_ImageUtil_textWaterMark(JNIEnv *env, jobject, jintArray pixels, jint width, jint height,
                                         jintArray textPixels, jint textWidth, jint textHeight){
    //像素数组拷贝到native层
    jint *pixel = env->GetIntArrayElements(pixels, false);
    jint *textPixel = env->GetIntArrayElements(textPixels, false);

    int size = width * height;
    int alpha, red, green, blue;
    int text_alpha, text_red, text_green, text_blue;

    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;

            //获取text的像素点,与原图像素相加
            if(x < textWidth && y < textHeight){
                int textColor = textPixel[y*textWidth + x];
                text_alpha = (textColor >> 24) & 0xFF;
                text_red = (textColor >> 16) & 0xFF;
                text_green = (textColor >> 8) & 0xFF;
                text_blue = textColor & 0xFF;

                //新的RGB分量=图片分量(R/G/B) + 文本分量(R/G/B)
                red   = (1-text_alpha)*red  + text_alpha*text_red;
                green = (1-text_alpha)*green + text_alpha*text_green;
                blue  = (1-text_alpha)*blue + text_alpha*text_blue;

                //边界检测:0<rgb<255
                red = red > 255 ? 255 : (red < 0 ? 0 : red);
                green = green > 255 ? 255 : (green < 0 ? 0 : green);
                blue = blue > 255 ? 255 : (blue < 0 ? 0 : blue);
            }

            //重新赋值给每个像素点
            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(textPixels, textPixel, 0);
    return newPixel;
}
        java层调用native层的文字水印处理方法,传入原图像素数组、原图宽度、原图高度、水印的像素数组、水印宽度、水印高度等参数。等待native层处理完成返回新数组,然后根据新数组创建新的Bitmap:

    /**
     * 使用NDK向图片添加文字水印
     * @param drawable drawable
     * @param imageView imageView
     * @param text 文本内容
     */
    private void textWaterMark(Drawable drawable, ImageView imageView, String text){
        if (drawable != null && drawable instanceof BitmapDrawable) {
            Bitmap bitmap = ((BitmapDrawable)drawable).getBitmap();
            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);

            //文本转成bitmap,再获取像素数组
            Bitmap textBitmap = BitmapUtil.textToBitmap(text, Color.BLUE, 16, this);
            int textWidth = textBitmap.getWidth();
            int textHeight = textBitmap.getHeight();
            int[] textPixel = new int[textWidth * textHeight];
            textBitmap.getPixels(textPixel, 0, textWidth, 0, 0, textWidth, textHeight);

            //ndk处理像素点数组
            int[] newPixels = ImageUtil.textWaterMark(pixels, width, height, textPixel, textWidth, textHeight);
            Bitmap ndkBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
            //重新设置bitmap的所有pixel像素点
            ndkBitmap.setPixels(newPixels, 0, width, 0, 0, width, height);
            imageView.setImageBitmap(ndkBitmap);
        }
    }
        除此之外,需要加载so库,声明native方法供java调用:

    static {
        System.loadLibrary("image");
    }

    public static native int[] textWaterMark(int[] pixels, int width, int height,
                                         int[] textPixels, int textWidth, int textHeight);
        这样看起来,只是在原图左上角添加水印,不够灵活。那么问题来了,有没办法去灵活控制水印在原图的位置呢?答案是肯定的,只需要传入x、y坐标的偏移量,native层加上偏移量处理就可以了。代码如下:

jintArray
Java_com_frank_image_ImageUtil_textWaterMark(JNIEnv *env, jobject, jintArray pixels, jint width, jint height,
                                         jintArray textPixels, jint textWidth, jint textHeight,
                                         jint offsetX, jint offsetY){
    //像素数组拷贝到native层
    jint *pixel = env->GetIntArrayElements(pixels, false);
    jint *textPixel = env->GetIntArrayElements(textPixels, false);

    int size = width * height;
    int alpha, red, green, blue;
    int text_alpha, text_red, text_green, text_blue;

    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;

            //获取text的像素点,与原图像素相加(加上偏移量判断,控制水印显示位置)
            if(x > offsetX && x < (textWidth+offsetX) && y > offsetY && y < (textHeight+offsetY)){
                int textColor = textPixel[(y-offsetY)*textWidth + (x-offsetX)];
                text_alpha = (textColor >> 24) & 0xFF;
                text_red = (textColor >> 16) & 0xFF;
                text_green = (textColor >> 8) & 0xFF;
                text_blue = textColor & 0xFF;

                //新的RGB分量=图片分量(R/G/B) + 文本分量(R/G/B)
                red   = (1-text_alpha)*red  + text_alpha*text_red;
                green = (1-text_alpha)*green + text_alpha*text_green;
                blue  = (1-text_alpha)*blue + text_alpha*text_blue;

                //边界检测:0<rgb<255
                red = red > 255 ? 255 : (red < 0 ? 0 : red);
                green = green > 255 ? 255 : (green < 0 ? 0 : green);
                blue = blue > 255 ? 255 : (blue < 0 ? 0 : blue);
            }

            //重新赋值给每个像素点
            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(textPixels, textPixel, 0);
    return newPixel;
}

        同样地,添加图片水印的native方法如下:

jintArray
Java_com_frank_image_ImageUtil_picWaterMark(JNIEnv *env, jobject, jintArray pixels, jint width, jint height,
                                            jintArray overlayPixels, jint overlayWidth, jint overlayHeight, 
                                            jint offsetX, jint offsetY){
    //像素数组拷贝到native层
    jint *pixel = env->GetIntArrayElements(pixels, false);
    jint *overlayPixel = env->GetIntArrayElements(overlayPixels, false);


    int size = width * height;
    int alpha, red, green, blue;


    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 > offsetX && x < (overlayWidth+offsetX) && y > offsetY && y < (overlayHeight+offsetY)){
                int addColor = overlayPixel[(y-offsetY)*overlayWidth + (x-offsetX)];
                //替换原图像素的RGBA
                alpha = (addColor >> 24) & 0xFF;
                red = (addColor >> 16) & 0xFF;
                green = (addColor >> 8) & 0xFF;
                blue = addColor & 0xFF;
            }


            //重新赋值给每个像素点
            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(overlayPixels, overlayPixel, 0);
    return newPixel;
}

        加上x、y坐标偏移量的添加水印效果如下:




        欢迎各位图片处理、多媒体开发爱好者相互交流,相互学习。。。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

徐福记456

您的鼓励和肯定是我创作动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值