NDK处理图片——添加文字和图片水印
来源:互联网 发布:淘宝达人登录网址 编辑:程序博客网 时间:2024/05/22 06:42
马化腾说过腾讯的服务器一天存储几个亿的图片,可见图片已经成为移动社交必不可少的元素。而用户往往喜欢在原图基础上编辑文字或者添加配图,达到图文并茂、生动形象的目的,以表达此时此地此景此物。添加水印已成为美图秀秀、QQ天天P图、各大直播平台的必备技能。接下来给大家介绍下,这是基于android移动端的NDK处理图片,添加文字或者图片水印。
先看下添加水印后的效果:
一张图片是由宽x高的二维矩阵像素点构成的,添加水印就是将覆盖层的矩阵像素与原图矩阵像素叠加。而一个像素是由ARGB(Alpha、Red、Green、Blue)组成,像素叠加需要拆分成ARGB分量进行叠加。native层处理代码如下:
jintArrayJava_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层加上偏移量处理就可以了。代码如下:
jintArrayJava_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方法如下:
jintArrayJava_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坐标偏移量的添加水印效果如下:
欢迎各位图片处理、多媒体开发爱好者相互交流,相互学习。。。
阅读全文