Android高效实现图片高斯模糊效果

2018-03-17  本文已影响0人  依玲之风

目前可以做图片高斯模糊的第三方图片加载工具有很多,比如现在用得比较火的图片加载工具:glide等工具都可以完成图片的高斯模糊。现在很多的APP都有使用图片的高斯模糊效果做背景图片,比如我们比较熟悉的:QQ音乐,酷狗音乐等软件都有体现。
今天我就不讲glide这个工具是如何使用做出图片高斯模糊的效果了,我用c的方式实现图片的高斯模糊效果。

要做这个效果首先我们要知道图片都是由RGB三个颜色组成的,那么我们就要拿到这个三个颜色块的数据出来做放大处理。

下面就开始写代码吧。

先声明两个原生方法:

public static native void blurBitMap(Bitmap,bitmap,int r);

public static native void blurIntArray(int[] pImg, int w, int h, int r);

解释一下上面的方法中的参数:
第一个方法中的第一个参数就不用多解释了,就解释一下第二个参数:r;这个“r”表示高斯模糊的半径。这个r越大图片的模糊效果就越明显,反之相反。
第二个方法;第一个参数是一个int类型的数组,这个是Bitmap的像素数组,第二第三个是Bitmap图片的宽高,第四个参数r和上述一样。
现在就在c++中实现上述的两个native方法

JNIEXPORT void JNICALL Java_com_xiaowei_imageHepler_ImageHepler_blurBitMap
  (JNIEnv *env, jclass obj, jobject bitmapIn, jint r){
            AndroidBitmapInfo infos;//这个BitmapInfo是保存着图片信息的结构体
    void* pixelsIn;//图片像素数组
    int ret;
    //判断BitmapInfos是否为空
    /**
     * 在用AndroidBitmap_lockPixels,AndroidBitmap_unlockPixels时要在Android.mk里成加上:LOCAL_LDLIBS    := -ljnigraphics这个要不然编译时会报错找不到这两个API
     */
    if((ret = AndroidBitmap_getInfo(env, bitmapIn, &infos)) < 0){
        return;
    }
    //如果不等于ANDROID_BITMAP_FORMAT_RGBA_8888说明不是image
    if(infos.format!=ANDROID_BITMAP_FORMAT_RGBA_8888){
        return;
    }
    //锁定image
    if((ret = AndroidBitmap_lockPixels(env, bitmapIn, &pixelsIn)) < 0){

    }
    int h = infos.height;
    int w = infos.width;
    pixelsIn = StackBlur((int*)pixelsIn, w, h, r);
    //到这里已经高斯模糊完成,下面把画布解锁
    AndroidBitmap_unlockPixels(env, bitmapIn);
}

使用AndroidBitmapInfo这个结构体,需要引入“android/bitmap.h”这个头文件

#include<android/bitmap.h>

在c中我们要对Bitmap图片进行操作就要用到AndroidBitmapInfo这个结构体,这个结构体里面包含了Bitmap图片的基本信息;比如:图片的宽、高等信息。

/** Bitmap info, see AndroidBitmap_getInfo(). */
typedef struct {
    /** The bitmap width in pixels. */
    uint32_t    width;
    /** The bitmap height in pixels. */
    uint32_t    height;
    /** The number of byte per row. */
    uint32_t    stride;
    /** The bitmap pixel format. See {@link AndroidBitmapFormat} */
    int32_t     format;
    /** Unused. */
    uint32_t    flags;      // 0 for now
} AndroidBitmapInfo;

上述是原码里面对AndroidBitmapInfo这个结构体的解释。
读取图片的信息使用“AndroidBitmap_getInfo()”这个函数。读取到的图片信息就会保存到“AndroiodBitmapInfo”这个结构体中保存。
下面实现第二个原生方法:

JNIEXPORT void JNICALL Java_com_xiaowei_imageHepler_ImageHepler_blurIntArray
  (JNIEnv *env, jclass obj, jintArray arrIn, jint w, jint h, jint r){
            jint *pix;
    pix = env->GetIntArrayElements(arrIn, 0);
    if (pix == NULL)
    return;
    pix = StackBlur(pix, w, h, r);
    env->ReleaseIntArrayElements(arrIn, pix, 0);
}

上面的原生方法中真正对图片高斯模糊是StackBlur这个函数。这个是函数就是对图片的rgb像素信息进行处理的;下面是这个函数的原码,在这里我就不一一解释了,直接上代码:

static int* StackBlur(int* pix, int w, int h, int radius) {
    int wm = w - 1;
    int hm = h - 1;
    int wh = w * h;
    int div = radius + radius + 1;
    //Ϊr,g,b�������ؿ����ڴ�ռ�
    int *r = (int *)  (wh * sizeof(int));
    int *g = (int *)malloc(wh * sizeof(int));
    int *b = (int *)malloc(wh * sizeof(int));
    int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;

    int *vmin = (int *)malloc(MAX(w,h) * sizeof(int));

    int divsum = (div + 1) >> 1;
    divsum *= divsum;
    int *dv = (int *)malloc(256 * divsum * sizeof(int));
    for (i = 0; i < 256 * divsum; i++) {
        dv[i] = (i / divsum);
    }

    yw = yi = 0;

    int(*stack)[3] = (int(*)[3])malloc(div * 3 * sizeof(int));
    int stackpointer;
    int stackstart;
    int *sir;
    int rbs;
    int r1 = radius + 1;
    int routsum, goutsum, boutsum;
    int rinsum, ginsum, binsum;

    for (y = 0; y < h; y++) {
        rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
        for (i = -radius; i <= radius; i++) {
            p = pix[yi + (MIN(wm, MAX(i, 0)))];
            sir = stack[i + radius];
            sir[0] = (p & 0xff0000) >> 16;
            sir[1] = (p & 0x00ff00) >> 8;
            sir[2] = (p & 0x0000ff);

            rbs = r1 - ABS(i);
            rsum += sir[0] * rbs;
            gsum += sir[1] * rbs;
            bsum += sir[2] * rbs;
            if (i > 0) {
                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];
            }
            else {
                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];
            }
        }
        stackpointer = radius;

        for (x = 0; x < w; x++) {

            r[yi] = dv[rsum];
            g[yi] = dv[gsum];
            b[yi] = dv[bsum];

            rsum -= routsum;
            gsum -= goutsum;
            bsum -= boutsum;

            stackstart = stackpointer - radius + div;
            sir = stack[stackstart % div];

            routsum -= sir[0];
            goutsum -= sir[1];
            boutsum -= sir[2];

            if (y == 0) {
                vmin[x] = MIN(x + radius + 1, wm);
            }
            p = pix[yw + vmin[x]];

            sir[0] = (p & 0xff0000) >> 16;
            sir[1] = (p & 0x00ff00) >> 8;
            sir[2] = (p & 0x0000ff);

            rinsum += sir[0];
            ginsum += sir[1];
            binsum += sir[2];

            rsum += rinsum;
            gsum += ginsum;
            bsum += binsum;

            stackpointer = (stackpointer + 1) % div;
            sir = stack[(stackpointer) % div];

            routsum += sir[0];
            goutsum += sir[1];
            boutsum += sir[2];

            rinsum -= sir[0];
            ginsum -= sir[1];
            binsum -= sir[2];

            yi++;
        }
        yw += w;
    }
    for (x = 0; x < w; x++) {
        rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
        yp = -radius * w;
        for (i = -radius; i <= radius; i++) {
            yi = MAX(0, yp) + x;

            sir = stack[i + radius];
;
            sir[2] = b[yi];
            sir[0] = r[yi];
            sir[1] = g[yi]

            rbs = r1 - ABS(i);

            rsum += r[yi] * rbs;
            gsum += g[yi] * rbs;
            bsum += b[yi] * rbs;

            if (i > 0) {
                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];
            }
            else {
                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];
            }

            if (i < hm) {
                yp += w;
            }
        }
        yi = x;
        stackpointer = radius;
        for (y = 0; y < h; y++) {

            pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];

            rsum -= routsum;
            gsum -= goutsum;
            bsum -= boutsum;

            stackstart = stackpointer - radius + div;
            sir = stack[stackstart % div];

            routsum -= sir[0];
            goutsum -= sir[1];
            boutsum -= sir[2];

            if (x == 0) {
                vmin[y] = MIN(y + r1, hm) * w;
            }
            p = x + vmin[y];

            sir[0] = r[p];
            sir[1] = g[p];
            sir[2] = b[p];

            rinsum += sir[0];
            ginsum += sir[1];
            binsum += sir[2];

            rsum += rinsum;
            gsum += ginsum;
            bsum += binsum;

            stackpointer = (stackpointer + 1) % div;
            sir = stack[stackpointer];

            routsum += sir[0];
            goutsum += sir[1];
            boutsum += sir[2];

            rinsum -= sir[0];
            ginsum -= sir[1];
            binsum -= sir[2];
            yi += w;
        }
    }
    free(r);
    free(g);
    free(b);
    free(vmin);
    free(dv);
    free(stack);
    return(pix);
}

这个函数就是对图片的像素信息进行处理后得到高斯模糊的效果,如果有兴趣的小伙伴可以看看。
这里要注意思一点:图片传到原生方法,由于c/c++代码里面没有对图片进行创建副本,直接对图片进行了高斯模糊了,如果图片还要在其他地方显示时,java层要对图片进行创建副本,或者在c/c++里面创建副本。我这里就不创建了。
好啦!今天就到这里了,如果小伙伴有什么不理解的可以留言,或者上述有什么写错的地方也可以留言提出来,我们大家一起共同进步。

上一篇下一篇

猜你喜欢

热点阅读