您的当前位置:首页正文

[Android]JNI进阶--图片实例(Mat与Bitmap)

来源:要发发知识网

Bitmap是Android中一个很重要都类,是一个图片信息类,图片都会封装成一个Bitmap的对象后,才能在Android中使用。
那么Bitmap究竟包含了什么东西呢?
就看几个比较重要的属性吧

   //jni分配的native空间地址
 // Convenience for JNI access
    private final long mNativePtr;

    private final boolean mIsMutable;

    /**
     * Represents whether the Bitmap's content is requested to be pre-multiplied.
     * Note that isPremultiplied() does not directly return this value, because
     * isPremultiplied() may never return true for a 565 Bitmap or a bitmap
     * without alpha.
     *
     * setPremultiplied() does directly set the value so that setConfig() and
     * setPremultiplied() aren't order dependent, despite being setters.
     *
     * The native bitmap's premultiplication state is kept up to date by
     * pushing down this preference for every config change.
     */
    private boolean mRequestPremultiplied;
    //标志点9图的数据
    private byte[] mNinePatchChunk; // may be null
    private NinePatch.InsetStruct mNinePatchInsets; // may be null
    //宽
    private int mWidth;
    //高
    private int mHeight;
    //是否回收
    private boolean mRecycled;

我们基本构造Bitmap都是使用BitmapFactory来进行构造的,其内部最终会调用这些native的方法

    private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
            Rect padding, Options opts);
    private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,
            Rect padding, Options opts);
    private static native Bitmap nativeDecodeAsset(long nativeAsset, Rect padding, Options opts);
    private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,
            int length, Options opts);

会调用nativeDecodeStream来解析文件流


nativeDecodeStream.png

配置完成后会调用Bitmap.cpp


createBitmap.png
可以看出是使用gBitmap_class的构造方法来创建对象
createBitmap.png

其实际还是会反射到framework 层的Bitmap层去创建一个java层表象,实际对象还是通过c++来分配内存地址的


反射Framework层Bitmap创建.png

Mat对象,是Opencv解析图片和图片处理的对象,以下是native层的bitmap转为Mat对象的逻辑

void bitmap_to_mat(JNIEnv *env, jobject &srcBitmap, Mat &srcMat) {
    void *srcPixels = 0;
    //native层的Bitmap对象信息
    AndroidBitmapInfo srcBitmapInfo;
    try {
        //转换层native层对象
        AndroidBitmap_getInfo(env, srcBitmap, &srcBitmapInfo);
        //加锁
        AndroidBitmap_lockPixels(env, srcBitmap, &srcPixels);
        uint32_t srcHeight = srcBitmapInfo.height;
        uint32_t srcWidth = srcBitmapInfo.width;
        //通道分解
        srcMat.create(srcHeight, srcWidth, CV_8UC4);
        if (srcBitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
            //转为4通道的Mat图
            Mat tmp(srcHeight, srcWidth, CV_8UC4, srcPixels);
            tmp.copyTo(srcMat);
        } else {
            //转为2通道的Mat灰度图
            Mat tmp = Mat(srcHeight, srcWidth, CV_8UC2, srcPixels);
            cvtColor(tmp, srcMat, COLOR_BGR5652RGBA);
        }
        //解锁
        AndroidBitmap_unlockPixels(env, srcBitmap);
        return;
    } catch (cv::Exception &e) {
        AndroidBitmap_unlockPixels(env, srcBitmap);
        jclass je = env->FindClass("java/lang/Exception");
        env -> ThrowNew(je, e.what());
        return;
    } catch (...) {
        AndroidBitmap_unlockPixels(env, srcBitmap);
        jclass je = env->FindClass("java/lang/Exception");
        env -> ThrowNew(je, "unknown");
        return;
    }
}

当然经过处理后还需要从Mat对象转换为bitmap对象,这种方式是外部传递了bitmap对象了。

void mat_to_bitmap(JNIEnv *env, Mat &srcMat, jobject &dstBitmap) {
    void *dstPixels = 0;
    AndroidBitmapInfo dstBitmapInfo;
    try {
        AndroidBitmap_getInfo(env, dstBitmap, &dstBitmapInfo);
        AndroidBitmap_lockPixels(env, dstBitmap, &dstPixels);
        uint32_t dstHeight = dstBitmapInfo.height;
        uint32_t dstWidth = dstBitmapInfo.width;
        //通道选择
        if (dstBitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
            Mat tmp(dstHeight, dstWidth, CV_8UC4, dstPixels);
            if(srcMat.type() == CV_8UC1) {
                //灰度图转为RGBA
                cvtColor(srcMat, tmp, COLOR_GRAY2RGBA);
            } else if (srcMat.type() == CV_8UC3) {
                cvtColor(srcMat, tmp, COLOR_BGR2RGBA);
            } else if (srcMat.type() == CV_8UC4) {
                srcMat.copyTo(tmp);
            }
        } else {
            Mat tmp = Mat(dstHeight, dstWidth, CV_8UC2, dstPixels);
            if(srcMat.type() == CV_8UC1) {
                cvtColor(srcMat, tmp, COLOR_GRAY2RGB);
            } else if (srcMat.type() == CV_8UC3) {
                cvtColor(srcMat, tmp, COLOR_BGR5652RGB);
            } else if (srcMat.type() == CV_8UC4) {
                cvtColor(srcMat, tmp, COLOR_BGRA2RGB);
            }
        }
        AndroidBitmap_unlockPixels(env, dstBitmap);
    }catch (cv::Exception &e) {
        AndroidBitmap_unlockPixels(env, dstBitmap);
        jclass je = env->FindClass("java/lang/Exception");
        env -> ThrowNew(je, e.what());
        return;
    } catch (...) {
        AndroidBitmap_unlockPixels(env, dstBitmap);
        jclass je = env->FindClass("java/lang/Exception");
        env -> ThrowNew(je, "unknown");
        return;
    }
}

下面这种是自身构成通过反射来构成好bitmap对象,这里仲带你是在反射构成bitmap对象并且拥有反射缓存,需要使用GlobalRef

jclass clazz;
jclass configCls;
jobject config;

jobject mat_to_bitmap_object(JNIEnv *env,Mat &srcMat){
    if(clazz == nullptr) {
        jclass jclazz = env->FindClass("android/graphics/Bitmap");
        clazz = (jclass)env->NewGlobalRef(jclazz);
    }
    jmethodID createBitmap = env->GetStaticMethodID(clazz, "createBitmap",
                                             "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
    if(configCls == nullptr) {
        jclass jconfigCls = env->FindClass("android/graphics/Bitmap$Config");
        configCls = (jclass)env->NewGlobalRef(jconfigCls);
    }
    jfieldID fid = env->GetStaticFieldID(configCls, "ARGB_8888", "Landroid/graphics/Bitmap$Config;");

    if (config == nullptr) {
        jobject jconfig = env->GetStaticObjectField(configCls, fid);
        config = env->NewGlobalRef(jconfig);
    }

    jobject jbitmap = env->CallStaticObjectMethod(clazz,createBitmap,srcMat.cols,srcMat.rows,config);

    AndroidBitmapInfo info;
    void *pixels = 0;
    try {
        AndroidBitmap_getInfo(env, jbitmap, &info);
        AndroidBitmap_lockPixels(env,jbitmap, &pixels);
        Mat tmp(info.height,info.width,CV_8UC4,pixels);
        cvtColor(srcMat,tmp,COLOR_BGR2RGBA);
        AndroidBitmap_unlockPixels(env,jbitmap);
        return jbitmap;
    }catch (cv::Exception &e) {
        AndroidBitmap_unlockPixels(env, jbitmap);
        return NULL;
    } catch (...) {
        AndroidBitmap_unlockPixels(env, jbitmap);
        return NULL;
    }
}

两个群号都可以加入,群2群号763094035,我在这里期待你们的加入!!!

Android组件化群2

群1号是316556016。

Android组件化群1