专栏首页Android 开发学习Android Studio 2.2 JNI编译及Rxjava使用初级背景

Android Studio 2.2 JNI编译及Rxjava使用初级背景

jni-2.png

背景

最近几天刚好无事由于在新公司,业务上安排不是很满。android studio 2.2以后,jni比较方便开发了。本文是使用jni进行初级的demo需求。一个图片的高斯模糊效果。算法参见(https://github.com/GankLi/Demo/tree/demo/app/src/main/java/com/gank/demo/gaussblurtest)。 上图是未模糊前原图,恩,我会把它模糊滴。

前提

请下下好ndk和cmake工具。需要环境android studio 2.2

down.jpeg

快速入门

总的工程图如图:

总的框架.png

jni配置想具体了解的可以看官方,本文仅提供一个简要的模式。先上gradle配置

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion '23.0.2'
    defaultConfig {
        applicationId 'com.example.hellojni'
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
        externalNativeBuild {
            cmake {
                arguments '-DANDROID_TOOLCHAIN=clang'
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.github.bumptech.glide:glide:3.7.0'
    compile 'io.reactivex:rxjava:1.1.0'
    compile 'io.reactivex:rxandroid:1.1.0'
}

其中jni相关的

externalNativeBuild {
            cmake {
                arguments '-DANDROID_TOOLCHAIN=clang'
            }
        }

cmake,这里指明了clang来作为编译器,这里其他配置参见官方。

 externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
        }
    }

这里指明make file相关的编译文件路径。

BlurUtil.java

package com.example.nothing.blurdemo;

import android.graphics.Bitmap;

public class BlurUtil {
    //分别在x轴 和 y轴方向上进行高斯模糊
    public static Bitmap gaussBlurUseGauss(Bitmap bitmap, int radius) {
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();

        //生成一张新的图片
        Bitmap outBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);

        //定义一个临时数组存储原始图片的像素 值
        int[] pix = new int[w * h];

        //将图片像素值写入数组
        bitmap.getPixels(pix, 0, w, 0, 0, w, h);

        //进行模糊
        initCBlur1(pix, w, h, radius);

        //将数据写入到 图片
        outBitmap.setPixels(pix, 0, w, 0, 0, w, h);

        //返回结果
        return outBitmap;
    }

    //利用均值模糊 逼近 高斯模糊
    public static Bitmap gaussBlurUseAvg(Bitmap bitmap, int radius) {
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();

        //生成一张新的图片
        Bitmap outBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);

        //定义一个临时数组存储原始图片的像素 值
        int[] pix = new int[w * h];

        //将图片像素值写入数组
        bitmap.getPixels(pix, 0, w, 0, 0, w, h);

        //进行模糊
        initCBlur2(pix, w, h, radius);

        //将数据写入到 图片
        outBitmap.setPixels(pix, 0, w, 0, 0, w, h);

        //返回结果
        return outBitmap;
    }

    //原始的高斯模糊 方法
    private static native void initCBlur1(int[] pix, int w, int h, int r);

    //利用均值模糊进行拟合 高斯模糊
    private static native void initCBlur2(int[] pix, int w, int h, int r);

    //加载native模块
    static {
        System.loadLibrary("hello-jni");
    }
}

主要注意加载模块。

hello-jni.cpp jni文件

由于高斯模糊,代码不多,就写到同一个cpp文件中。

#include <jni.h>
#include <android/log.h>
#include <iostream>
#include <cmath>

#define  LOG_TAG    "blur"
#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)

#define PI 3.1415926


extern "C" {

//LOGD("jni %d:  %lf", i, amplitude[i]);
void gaussBlur1(int *pix, int w, int h, int radius) {
    float sigma = 1.0 * radius / 2.57;    //2.57 * sigam半径之后基本没有贡献 所以取sigma为 r / 2.57
    float deno = 1.0 / (sigma * sqrt(2.0 * PI));
    float nume = -1.0 / (2.0 * sigma * sigma);

    //高斯分布产生的数组
    float *gaussMatrix = (float *) malloc(sizeof(float) * (radius + radius + 1));
    float gaussSum = 0.0;
    for (int i = 0, x = -radius; x <= radius; ++x, ++i) {
        float g = deno * exp(1.0 * nume * x * x);

        gaussMatrix[i] = g;
        gaussSum += g;
    }

    //归1话
    int len = radius + radius + 1;
    for (int i = 0; i < len; ++i)
        gaussMatrix[i] /= gaussSum;

    //临时存储 一行的数据
    int *rowData = (int *) malloc(w * sizeof(int));
    int *listData = (int *) malloc(h * sizeof(int));

    //x方向的模糊
    for (int y = 0; y < h; ++y) {
        //拷贝一行数据
        memcpy(rowData, pix + y * w, sizeof(int) * w);

        for (int x = 0; x < w; ++x) {
            float r = 0, g = 0, b = 0;
            gaussSum = 0;

            for (int i = -radius; i <= radius; ++i) {
                int k = x + i;

                if (0 <= k && k <= w) {
                    //得到像素点的rgb值
                    int color = rowData[k];
                    int cr = (color & 0x00ff0000) >> 16;
                    int cg = (color & 0x0000ff00) >> 8;
                    int cb = (color & 0x000000ff);

                    r += cr * gaussMatrix[i + radius];
                    g += cg * gaussMatrix[i + radius];
                    b += cb * gaussMatrix[i + radius];

                    gaussSum += gaussMatrix[i + radius];
                }
            }

            int cr = (int) (r / gaussSum);
            int cg = (int) (g / gaussSum);
            int cb = (int) (b / gaussSum);

            pix[y * w + x] = cr << 16 | cg << 8 | cb | 0xff000000;
        }
    }

    for (int x = 0; x < w; ++x) {
        //拷贝 一列 数据
        for (int y = 0; y < h; ++y)
            listData[y] = pix[y * w + x];

        for (int y = 0; y < h; ++y) {
            float r = 0, g = 0, b = 0;
            gaussSum = 0;

            for (int j = -radius; j <= radius; ++j) {
                int k = y + j;

                if (0 <= k && k <= h) {
                    int color = listData[k];
                    int cr = (color & 0x00ff0000) >> 16;
                    int cg = (color & 0x0000ff00) >> 8;
                    int cb = (color & 0x000000ff);

                    r += cr * gaussMatrix[j + radius];
                    g += cg * gaussMatrix[j + radius];
                    b += cb * gaussMatrix[j + radius];

                    gaussSum += gaussMatrix[j + radius];
                }
            }

            int cr = (int) (r / gaussSum);
            int cg = (int) (g / gaussSum);
            int cb = (int) (b / gaussSum);

            pix[y * w + x] = cr << 16 | cg << 8 | cb | 0xff000000;
        }
    }

    //清理内存
    free(gaussMatrix);
    free(rowData);
    free(listData);
}


//参考:http://blog.ivank.net/fastest-gaussian-blur.html
//横向的均值模糊 srcPix:原始的像素值 destPix将处理过的像素值放入到 destPix中
void boxBlurH(int *srcPix, int *destPix, int w, int h, int radius) {
    //用于索引
    int index;

    //r g b在遍历是 累加的色彩通道的总和
    int a = 0, r = 0, g = 0, b = 0;
    int ta, tr, tg, tb;    //临时变量

    //临时变量
    int color;
    int preColor;

    //用于计算权值 1 / num
    int num;
    float iarr;

    for (int i = 0; i < h; ++i) {
        r = 0;
        g = 0;
        b = 0;

        index = i * w;
        num = radius;

        for (int j = 0; j < radius; j++) {
            //累加0,radius-1的色彩的总和
            color = srcPix[index + j];
            //a += (color & 0xff000000) >> 24;
            r += (color & 0x00ff0000) >> 16;
            g += (color & 0x0000ff00) >> 8;
            b += (color & 0x000000ff);
        }

        //真正开始计算
        for (int j = 0; j <= radius; ++j) {
            num++;
            iarr = 1.0 / (1.0 * num);

            color = srcPix[index + j + radius];
            //a += (color & 0xff000000) >> 24;
            r += (color & 0x00ff0000) >> 16;
            g += (color & 0x0000ff00) >> 8;
            b += (color & 0x000000ff);

            //ta = (int)(1.0 * a / num);
            tr = (int) (r * iarr);
            tg = (int) (g * iarr);
            tb = (int) (b * iarr);

            destPix[index + j] = tr << 16 | tg << 8 | tb | 0xff000000;
        }

        iarr = 1.0 / (1.0 * num);
        for (int j = radius + 1; j < w - radius; ++j) {
            preColor = srcPix[index + j - 1 - radius];
            color = srcPix[index + j + radius];

            //a += (color & 0xff000000) >> 24 - (preColor & 0xff000000) >> 24;
            r = r + ((color & 0x00ff0000) >> 16) - ((preColor & 0x00ff0000) >> 16);
            g = g + ((color & 0x0000ff00) >> 8) - ((preColor & 0x0000ff00) >> 8);
            b = b + (color & 0x000000ff) - (preColor & 0x000000ff);

            //ta = (int)(1.0 * a / num);
            tr = (int) (r * iarr);
            tg = (int) (g * iarr);
            tb = (int) (b * iarr);

            destPix[index + j] = tr << 16 | tg << 8 | tb | 0xff000000;
        }

        for (int j = w - radius; j < w; ++j) {
            num--;
            iarr = 1.0 / (1.0 * num);

            preColor = srcPix[index + j - 1 - radius];

            //a -= (preColor & 0xff000000) >> 24;
            r -= (preColor & 0x00ff0000) >> 16;
            g -= (preColor & 0x0000ff00) >> 8;
            b -= (preColor & 0x000000ff);

            //ta = (int)(1.0 * a / num);
            tr = (int) (r * iarr);
            tg = (int) (g * iarr);
            tb = (int) (b * iarr);

            //
            //destPix[index + j] = (ta << 24 | tr << 16 | tg << 8 | tb);
            destPix[index + j] = tr << 16 | tg << 8 | tb | 0xff000000;
        }
    }
}


//列的均值模糊 srcPix:原始的像素值 destPix将处理过的像素值放入到 destPix中
void boxBlurV(int *srcPix, int *destPix, int w, int h, int radius) {
    //r g b在遍历是 累加的色彩通道的总和
    int a = 0, r = 0, g = 0, b = 0;
    int ta, tr, tg, tb;    //临时变量

    //临时变量
    int color;
    int preColor;

    //用于计算权值 1 / num
    int num;
    float iarr;

    for (int i = 0; i < w; ++i) {
        r = 0;
        g = 0;
        b = 0;

        num = radius;

        for (int j = 0; j < radius; ++j) {
            color = srcPix[j * w + i];
            r += (color & 0x00ff0000) >> 16;
            g += (color & 0x0000ff00) >> 8;
            b += (color & 0x000000ff);
        }

        for (int j = 0; j <= radius; ++j) {
            num++;
            iarr = 1.0 / (1.0 * num);

            color = srcPix[(j + radius) * w + i];
            r += (color & 0x00ff0000) >> 16;
            g += (color & 0x0000ff00) >> 8;
            b += (color & 0x000000ff);

            tr = (int) (r * iarr);
            tg = (int) (g * iarr);
            tb = (int) (b * iarr);

            destPix[j * w + i] = tr << 16 | tg << 8 | tb | 0xff000000;
        }

        iarr = 1.0 / (1.0 * num);
        for (int j = radius + 1; j < h - radius; ++j) {
            preColor = srcPix[(j - radius - 1) * w + i];
            color = srcPix[(j + radius) * w + i];

            r = r + ((color & 0x00ff0000) >> 16) - ((preColor & 0x00ff0000) >> 16);
            g = g + ((color & 0x0000ff00) >> 8) - ((preColor & 0x0000ff00) >> 8);
            b = b + (color & 0x000000ff) - (preColor & 0x000000ff);

            tr = (int) (r * iarr);
            tg = (int) (g * iarr);
            tb = (int) (b * iarr);

            destPix[j * w + i] = tr << 16 | tg << 8 | tb | 0xff000000;
        }

        for (int j = h - radius; j < h; ++j) {
            num--;
            iarr = 1.0 / (1.0 * num);
            preColor = srcPix[(j - radius - 1) * w + i];

            r -= (preColor & 0x00ff0000) >> 16;
            g -= (preColor & 0x0000ff00) >> 8;
            b -= (preColor & 0x000000ff);

            tr = (int) (r * iarr);
            tg = (int) (g * iarr);
            tb = (int) (b * iarr);

            destPix[j * w + i] = tr << 16 | tg << 8 | tb | 0xff000000;
        }
    }
}

void boxBlur(int *srcPix, int *destPix, int w, int h, int r) {
    if (r < 0) {
        LOGD("boxBlur r < 0: %d", r);
        return;
    }

    boxBlurH(srcPix, destPix, w, h, r);
    boxBlurV(destPix, srcPix, w, h, r);
}

//领用n 个 box 拟合 sigma的高斯函数
//参考:http://www.csse.uwa.edu.au/~pk/research/pkpapers/FastGaussianSmoothing.pdf
void boxesForGauss(float sigma, int *size, int n) {
    float wIdeal = sqrt(12.0 * sigma * sigma / n + 1.0);
    int wl = floor(wIdeal);

    if (0 == wl % 2)
        wl--;

    int wu = wl + 2;

    float mIdeal = (12.0 * sigma * sigma - n * wl * wl - 4 * n * wl - 3 * n) / (-4 * wl - 4);
    int m = round(mIdeal);

    for (int i = 0; i < n; ++i)
        size[i] = (i < m ? wl : wu);
}

void gaussBlur2(int *pix, int w, int h, int r) {
    float sigma = 1.0 * r / 2.57;    //2.57 *sigam半径之后基本没有贡献 所以取sigma为 r / 2.57

    int boxSize = 3;
    int *boxR = (int *) malloc(sizeof(int) * boxSize);    //需要的个数

    //计算拟合的半径
    boxesForGauss(sigma, boxR, boxSize);

    int *tempPix = (int *) malloc(sizeof(int) * w * h);

    boxBlur(pix, tempPix, w, h, (boxR[0] - 1) / 2);
    boxBlur(pix, tempPix, w, h, (boxR[1] - 1) / 2);
    boxBlur(pix, tempPix, w, h, (boxR[2] - 1) / 2);

    //清理内存
    free(boxR);
    free(tempPix);
}

void Java_com_example_nothing_blurdemo_BlurUtil_initCBlur1(JNIEnv *env,
                                                           jobject obj,
                                                           jintArray pix,
                                                           jint w,
                                                           jint h,
                                                           jint r) {
    gaussBlur1(env->GetIntArrayElements(pix, NULL), w, h, r);
}

void Java_com_example_nothing_blurdemo_BlurUtil_initCBlur2(JNIEnv *env,
                                                           jobject obj,
                                                           jintArray pix,
                                                           jint w,
                                                           jint h,
                                                           jint r) {
    gaussBlur2(env->GetIntArrayElements(pix, NULL), w, h, r);
}

}

注意这里: Java_com_example_nothing_blurdemo_BlurUtil 为BlurUtil类的全包路径。initCBlur1 方法名。JNIEnv 环境变量,jobject ,jintArray什么的,j代表java,去掉j就知道了。

void Java_com_example_nothing_blurdemo_BlurUtil_initCBlur1(JNIEnv *env,
                                                           jobject obj,
                                                           jintArray pix,
                                                           jint w,
                                                           jint h,
                                                           jint r) {
    gaussBlur1(env->GetIntArrayElements(pix, NULL), w, h, r);
}

CMakeLists.txt文件

cmake_minimum_required(VERSION 3.4.1)

add_library(hello-jni SHARED
            hello-jni.cpp)

# Include libraries needed for hello-jni lib
target_link_libraries(hello-jni log android)

add_library(hello-jni SHARED hello-jni.cpp) 三个参数 ,第一个为库名,第二个shared库(so)。最后一个参数是引入的文件。

MainActivity

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "nothingwxq";
    ImageView mImageView;
    private Subscriber<Bitmap> mBitmapSubscriber;
    private Subscription mSubscription;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mImageView = (ImageView) this.findViewById(R.id.image);

        mBitmapSubscriber = new Subscriber<Bitmap>() {
            @Override
            public void onCompleted() {
                Log.d(TAG,"onCompleted---");
            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onNext(Bitmap bitmap) {
                Log.d(TAG,"onNext---");
                mImageView.setImageBitmap(bitmap);
            }
        };

        Glide.with(getApplicationContext())
                .load("http://ww2.sinaimg.cn/mw1024/005yr3jYjw1f23h3vg4waj30qo0ziwlx.jpg")
                .asBitmap()
                .into(new SimpleTarget<Bitmap>() {
                    @Override
                    public void onResourceReady(final Bitmap bitmap, GlideAnimation anim) {
                        mSubscription = Observable
                                .create(new Observable.OnSubscribe<Bitmap>() {
                                    @Override
                                    public void call(Subscriber<? super Bitmap> subscriber) {
                                        Log.d(TAG, "creat---");
                                        subscriber.onNext(bitmap);
                                        subscriber.onCompleted();
                                    }
                                })
                                .observeOn(Schedulers.io())
                                .map(new Func1<Bitmap, Bitmap>() {
                                    @Override
                                    public Bitmap call(Bitmap bitmap) {
                                        Log.d(TAG, "map---");
                                        return BlurUtil.gaussBlurUseAvg(bitmap, 0);
                                    }
                                })
                                .subscribeOn(AndroidSchedulers.mainThread())
                                .observeOn(AndroidSchedulers.mainThread())
                                .subscribe(mBitmapSubscriber);
                    }
                });

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mSubscription.unsubscribe();
    }
}

本文省略资源文件,很简单的,一张图片。这里说下逻辑,使用Glide加载网上的图片,这里涉及Glide回调的监听。下载完成后,通过rxjava 的map 操作处理,最后onNext中进行设置图片,ok,完工。看下效果:

jni-3.png

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • ANR(网络资料整理)

    子勰
  • ant常见错误解决方案

    子勰
  • Eclipse使用中部分经验总结

    子勰
  • iMac(OS X)搭建私有maven仓库,提供Nexus Responsitory镜像

    子勰
  • Java--违例控制(异常处理)

    SuperHeroes
  • Java--类和对象之句柄、作用域

    SuperHeroes
  • Java--类和对象之基础知识

    SuperHeroes
  • Java--数据类型及类型转换

    SuperHeroes
  • Java--数组

    SuperHeroes
  • Java参数引用传递引发的惨案(又一次Java的String的“非对象”特性的踩坑经历)

    子勰

扫码关注云+社区

领取腾讯云代金券