马化腾说过腾讯的服务器一天存储几个亿的图片,可见图片已经成为移动社交必不可少的元素。而用户往往喜欢在原图基础上编辑文字或者添加配图,达到图文并茂、生动形象的目的,以表达此时此地此景此物。添加水印已成为美图秀秀、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坐标偏移量的添加水印效果如下:
欢迎各位图片处理、多媒体开发爱好者相互交流,相互学习。。。