【Androidメモリ最適化】Androidエンジニアリングでlibjpeg-turbo圧縮画像を使用(JNIよりBitmapを転送|ビットマップ情報取得|画像データ取得|画像データフィルタリング|リソース解放)


文書ディレクトリ
  • 一、Bitmap画像データ処理
  • 二、Java層BitmapオブジェクトからJNI層bitmapオブジェクトへの変換
  • 三、bitmap中の画像データ
  • を取得する
  • 四、bitmap中の画像データ(RGBデータを取得するAチャネルデータを除く)
  • をフィルタリングする.
  • 五、解放資源
  • 六、Bitmap画像データ処理
  • 前回ブログ【Androidメモリ最適化】libjpeg-turbo関数ライブラリクロスコンパイルと使用(クロスコンパイルスクリプト作成|関数ライブラリヘッダファイルコピー|構築スクリプト構成|Android Studioテスト関数ライブラリ)のlibjpeg-turbo関数ライブラリをクロスコンパイルし、対応するヘッダファイルと静的ライブラリをAndroid Studioプロジェクトにコピーし、CMakeListを構成した.txt構築スクリプト、build.gradle構築スクリプトは、このブログでコードの作成を開始します.
    一、Bitmap画像データ処理
    Bitmap画像データ処理:
    ①Bitmap画像オブジェクトを取得する:JavaがJNI層に渡すのはjobjectオブジェクトであり、これをJNI中のbitmapオブジェクトに変換する必要がある;
    ②データ抽出:bitmap画像からRGB画素値、すなわちALPHAチャネル(透明度)を除いたデータを抽出する.
    ③libjpeg-turboを用いてピクチャを圧縮する:libjpeg-turbo関数ライブラリを呼び出し、上記抽出したピクチャRGB画素データを圧縮する;
    ④リリースリソース:画像圧縮完了後、関連リソースをリリースする;
    二、Java層BitmapオブジェクトからJNI層bitmapオブジェクトへ
    1.Bitmap情報:AndroidBitmapInfo構造体には、画像幅、画像高さ、画素フォーマットなどの情報が封入されている.
    /** Bitmap info, see AndroidBitmap_getInfo(). */
    typedef struct {
        /**       . */
        uint32_t    width;
        /**       . */
        uint32_t    height;
        /**      . */
        uint32_t    stride;
        /**     . See {@link AndroidBitmapFormat} */
        int32_t     format;
        /**    . */
        uint32_t    flags;      // 0 for now
    } AndroidBitmapInfo;
    

    2.Bitmap情報を取得:bitmapを呼び出す.hのAndroid Bitmap_getInfoメソッドは、jbitmapから対応する情報を取得することができる.
    int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,
                              AndroidBitmapInfo* info);
    

    3.コード例:
        //        ,           
        //     bitmap.h
        AndroidBitmapInfo info;
        //   bitmap           AndroidBitmapInfo
        AndroidBitmap_getInfo(env, jbitmap, &info);
    

    三、bitmapの画像データを取得する
    Android Bitmapを呼び出すlockPixelsメソッドは、JavaのBitmapオブジェクトからデータのヘッダアドレスを取得できます.この関数に2 Dポインタが入力され、この2 Dポインタパラメータは戻り値として使用され、この2 Dポインタが最終的に指すメモリは画像データメモリである.
    1. AndroidBitmap_lockPixels関数の役割:指定されたJava Bitmapオブジェクトから、対応するピクセルデータアドレスを取得します.ロックは画素データメモリがロック解除方法を呼び出し、関連データを消去するまで固定されていることを保証することができる.この方法はAndroid Bitmap_とunlockPixelsメソッドはペアで使用され、その後addrPtrアドレスは使用されるべきではありません.実行に成功すると、*addrPtrは画像画素データの先頭アドレスを指し、方法が失敗した場合、この2次元ポインタは無効なポインタである.
    2. AndroidBitmap_lockPixels関数のプロトタイプ:
    ①JNIEnv*envパラメータ:注意ここではJNIEnv*envパラメータを使用しています.メインスレッド呼び出しは直接使用できます.サブスレッド呼び出しの場合、JavaVM呼び出しAttachCurrentThreadメソッドを使用してJNIEnvポインタを入力する必要があります.その後、このJNIEnvはスレッド対応のJNI環境で、使用が終わったらバインドを解除します.
    参考【Android NDK開発】JNIスレッド(JNIスレッド作成|スレッド実行関数|非JNIメソッド取得JNIEnvとJavaオブジェクト|スレッド取得JNIEnv|グローバル変数設定)ブログ;
    ②jobject jbitmapパラメータ:JavaのBitmapオブジェクト;
    ③void**addrPtrパラメータ:二次元ポインタ、実行成功後に画像の画素データの先頭アドレスを指し、同時に戻り値として使用し、ユーザーが画素データを呼び出すことができるようにする.
    int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr);
    

    3.コード例:
        //          unsigned char,     Java    byte
        //     byte   ,       
        //          
        uint8_t *addrPtr;
        //                 ,       ARGB
        AndroidBitmap_lockPixels(env, jbitmap, (void **) &addrPtr);
    

    四、bitmap中の画像データをフィルタリングする(RGBデータを取得してAチャネルデータを取り除く)
    1.データフィルタ需要:以前に画像データを取得し、addrPtrポインタに保存した.
    2.2つのメモリ:uint 8_t*addrPtrポインタが指すメモリはソースデータ、uint 8_t*dataポインタが指すデータはターゲットデータであり、最終的に圧縮するデータはdataターゲットデータである.
    3.画素フォーマット:ソースデータに格納されているBGRA画素フォーマットのデータであり、ターゲットデータに格納されているのはBGR画素フォーマットのデータである.
    4.画素データの循環:循環を開き、各画素点を直接循環し、画素点の保存形式がBGRAであることに注意し、その後、データを別のメモリに保存し、保存順序はBGRである.注意ループのたびに、対応するポインタを移動する必要があります.
        // JPEG      ,        ,       RGB
        //            
        //          
        int width = info.width;
        //          
        int height = info.height;
    
        //    RGB   
        uint8_t* data = (uint8_t *) malloc(width * height * 3);
    
        // data           ,           ,           
        uint8_t* temp = data;
    
        // JPEG      RGB    ,    
        uint8_t red, green, blue;
    
        //     Bitmap    addrPtr     BGRA   ,     data      BGR    
        for(int i = 0; i < height; i++){
            for (int j = 0; j < width; ++j) {
            	//    i   j        
                //   Bitmap          BGRA
                blue = *( addrPtr );
                green = *( addrPtr + 1 );
                red = *( addrPtr + 2 );
    
                // libturbojpeg   JPEG           BGR
                *data = blue;
                *( data + 1 ) = green;
                *( data + 2 ) = red;
    
                //    data   
                data += 3;
                //   addrPtr   ,            
                addrPtr +=4;
            }
        }//      ,       JPEG        ,   data    
    

    五、資源の解放
    前にjpegフォーマットの画像を圧縮するステップがありました.このプロセスは複雑で、ブログの説明を開きます.この章は圧縮が終わった後のメモリの解放操作を説明します.
    1.ピクセルデータのロック/ロック解除:Android Bitmap_UnlockPixelsメソッドとAndroid Bitmap_lockPixelsメソッドはペアで使用され、Bitmapオブジェクトのデータが不要になったことを示します.
    2.圧縮データの解放:圧縮するJPEGピクチャRGBデータを格納しているメモリを解放し、この時点で既に圧縮済みで、前に申請したメモリをすべて解放することができる.注意前に申請したdataポインタは、データをコピーする過程で、このポインタを移動して、dataポインタを解放することができなくて、前のdataメモリ申請後のバックアップポインタしか解放できなくて、さもなくばエラーを報告します;
    3.文字列の解放:env->GetStringUTFcharsが作成した文字列はローカル参照で、ここでは解放する必要があります.メモリをタイムリーに回収するのは良い習慣です.
        //   
        AndroidBitmap_unlockPixels(env,jbitmap);
        //       temp    ,       data   ,      
        free(temp);
        //       ,    , GC     ,      
        env->ReleaseStringUTFChars(path, filePath);
    

    六、Bitmap画像データ処理
    GitHubプロジェクトアドレス:han 1202012/PictureCompression
    libjpeg-turbo圧縮JPEGコード例:
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    //     
    void compressJpegFile(uint8_t *data, int imageWidth, int imageHeight,
                          jint compressQuality, const char *filename);
    
    extern "C" JNIEXPORT jstring JNICALL
    Java_kim_hsl_pc_MainActivity_stringFromJNI(
            JNIEnv* env,
            jobject /* this */) {
        std::string hello = "Hello from C++";
    
        //    libturbojpeg.a    
        jpeg_compress_struct jcs;
        __android_log_print(ANDROID_LOG_INFO, "JPEG", "jpeg_compress_struct jcs = %d", jcs.image_width);
        hello = hello + " , jpeg_compress_struct jcs = " + std::to_string(jcs.image_width);
    
        return env->NewStringUTF(hello.c_str());
    }
    
    /**
     *       
     */
    extern "C"
    JNIEXPORT void JNICALL
    Java_kim_hsl_pc_MainActivity_native_1pictureCompress(JNIEnv *env, jobject thiz,
                                                         jobject jbitmap,
                                                         jint quality, jstring path) {
    
        //   Java       C    ,         
        const char *filePath = env->GetStringUTFChars(path, 0);
    
        //        ,           
        //     bitmap.h
        AndroidBitmapInfo info;
        //   bitmap           AndroidBitmapInfo
        AndroidBitmap_getInfo(env, jbitmap, &info);
    
        //          unsigned char,     Java    byte
        //     byte   ,       
        //          
        uint8_t *addrPtr;
        //                 ,       ARGB
        AndroidBitmap_lockPixels(env, jbitmap, (void **) &addrPtr);
    
        // JPEG      ,        ,       RGB
        //            
        //          
        int width = info.width;
        //          
        int height = info.height;
    
        //rgb
        uint8_t* data = (uint8_t *) malloc(width * height * 3);
    
        // data           ,           ,           
        uint8_t* temp = data;
    
        // JPEG      RGB    ,    
        uint8_t red, green, blue;
    
        //     Bitmap    addrPtr     BGRA   ,     data      BGR    
        for(int i = 0; i < height; i++){
            for (int j = 0; j < width; ++j) {
                //   Bitmap          BGRA
                blue = *( addrPtr );
                green = *( addrPtr + 1 );
                red = *( addrPtr + 2 );
    
                // libturbojpeg   JPEG           BGR
                *data = blue;
                *( data + 1 ) = green;
                *( data + 2 ) = red;
    
                //    data   
                data += 3;
                //   addrPtr   ,            
                addrPtr +=4;
            }
        }//      ,       JPEG        ,   data    
    
        //   data           JPEG      
        compressJpegFile(temp, width, height, quality, filePath);
    
        //   
        AndroidBitmap_unlockPixels(env,jbitmap);
        //       temp    ,       data   ,      
        free(temp);
        //       ,    , GC     ,      
        env->ReleaseStringUTFChars(path, filePath);
    }