linux下編譯libjpeg-turbo Android中使用libjpeg

參考了這篇總結(jié)https://juejin.im/post/5cb1d6f7518825186d653aa7但惶,在此基礎(chǔ)上加入了一些遇到的bug以及解決方法

編譯環(huán)境

1. Ubuntu-18.04.1
2. ndk 13
3. libjpeg-turbo 最新源碼
4. cmake 3.12.1

編譯前準備工作

  1. 下載ndk
  2. 下載cmake
  3. 下載源碼
  4. 修改配置
    1. 打開 libjpeg-turbo/sharedLibs/CMakeList.txt, 將設(shè)置版本號的位置注釋, 否則在使用時, 可能會出現(xiàn)運行時缺少 so 庫的問題


      16a16af9e818ef73.png

編譯腳本

  1. 需要改動的地方就是 #1 #2 #3 替換成本地的環(huán)境

  2. 執(zhí)行腳本

  3. 編譯后so庫位置


    QQ圖片20190521193443.png
     #!/bin/sh
     # lib-name
     MY_LIBS_NAME=libjpeg-turbo
     # 源碼文件目錄        1
     MY_SOURCE_DIR=/home/lpb/libjpeg-turbo-source/libjpeg-turbo-master
     # 編譯的過程中產(chǎn)生的中間件的存放目錄芯义,為了區(qū)分編譯目錄丰辣,源碼目錄盔沫,install目錄
     MY_BUILD_DIR=binary
     
     ##  CMake 環(huán)境變量    2
     export PATH=/home/lpb/cmake/cmake-3.12.1-Linux-x86_64/bin:$PATH
     # 3
     NDK_PATH=/home/lpb/ndk/android-ndk-r13b-linux-x86_64/android-ndk-r13b
     BUILD_PLATFORM=linux-x86_64
     TOOLCHAIN_VERSION=4.9
     ANDROID_VERSION=19
     
     ANDROID_ARMV5_CFLAGS="-march=armv5te"
     ANDROID_ARMV7_CFLAGS="-march=armv7-a -mfloat-abi=softfp -mfpu=neon"  # -mfpu=vfpv3-d16  -fexceptions -frtti
     ANDROID_ARMV8_CFLAGS="-march=armv8-a"   # -mfloat-abi=softfp -mfpu=neon -fexceptions -frtti
     ANDROID_X86_CFLAGS="-march=i386 -mtune=intel -mssse3 -mfpmath=sse -m32"
     ANDROID_X86_64_CFLAGS="-march=x86-64 -msse4.2 -mpopcnt -m64 -mtune=intel"
     
     # params($1:arch,$2:arch_abi,$3:host,$4:compiler,$5:cflags,$6:processor)
     build_bin() {
     
         echo "-------------------star build $2-------------------------"
     
         ARCH=$1                # arm arm64 x86 x86_64
         ANDROID_ARCH_ABI=$2    # armeabi armeabi-v7a x86 mips
         # 最終編譯的安裝目錄
         PREFIX=$(pwd)/dist/${MY_LIBS_NAME}/${ANDROID_ARCH_ABI}/
         HOST=$3
         COMPILER=$4
         PROCESSOR=$6
         SYSROOT=${NDK_PATH}/platforms/android-${ANDROID_VERSION}/arch-${ARCH}
         CFALGS="$5"
         TOOLCHAIN=${NDK_PATH}/toolchains/${HOST}-${TOOLCHAIN_VERSION}/prebuilt/${BUILD_PLATFORM}
         
         # build 中間件
         BUILD_DIR=./${MY_BUILD_DIR}/${ANDROID_ARCH_ABI}
     
         export CFLAGS="$5 -Os -D__ANDROID_API__=${ANDROID_VERSION} --sysroot=${SYSROOT} \
                        -isystem ${NDK_PATH}/sysroot/usr/include \
                        -isystem ${NDK_PATH}/sysroot/usr/include/${HOST} "
         export LDFLAGS=-pie
     
         echo "path==>$PATH"
         echo "build_dir==>$BUILD_DIR"
         echo "ARCH==>$ARCH"
         echo "ANDROID_ARCH_ABI==>$ANDROID_ARCH_ABI"
         echo "HOST==>$HOST"
         echo "CFALGS==>$CFALGS"
         echo "COMPILER==>$COMPILER-gcc"
         echo "PROCESSOR==>$PROCESSOR"
     
         mkdir -p ${BUILD_DIR}   #創(chuàng)建當前arch_abi的編譯目錄,比如:binary/armeabi-v7a
         cd ${BUILD_DIR}         #此處 進了當前arch_abi的2級編譯目錄
     
     # 運行時創(chuàng)建臨時編譯鏈文件toolchain.cmake
     cat >toolchain.cmake << EOF 
     set(CMAKE_SYSTEM_NAME Linux)
     set(CMAKE_SYSTEM_PROCESSOR $6)
     set(CMAKE_C_COMPILER ${TOOLCHAIN}/bin/${COMPILER}-gcc)
     set(CMAKE_FIND_ROOT_PATH ${TOOLCHAIN}/${COMPILER})
     EOF
     
         cmake -G"Unix Makefiles" \
               -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake \
               -DCMAKE_POSITION_INDEPENDENT_CODE=1 \
               -DCMAKE_INSTALL_PREFIX=${PREFIX} \
               -DWITH_JPEG8=1 \
               ${MY_SOURCE_DIR}
     
         make clean
         make
         make install
     
         #從當前arch_abi編譯目錄跳出谊却,對應(yīng)上面的cd ${BUILD_DIR},以便function多次執(zhí)行
         cd ../../
     
         echo "-------------------$2 build end-------------------------"
     }
     
     # build armeabi 編譯armeabi
     build_bin arm armeabi arm-linux-androideabi arm-linux-androideabi "$ANDROID_ARMV5_CFLAGS" arm
    

Android環(huán)境配置

  1. 拷貝頭文件和so庫到對應(yīng)的位置

    1. so庫 :main/jniLibs/armeabi/
    2. 頭文件:cpp/include/
  2. 配置CMakeLists.txt

    1. 引入頭文件

           include_directories( ${CMAKE_SOURCE_DIR}/include)
      
    2. 添加庫名字

       add_library(
           libjpeg
           SHARED
           IMPORTED)
      
    3. 設(shè)置庫的屬性

        set_target_properties(
           libjpeg
           PROPERTIES
           IMPORTED_LOCATION
           //路徑需要寫絕對路徑
           C:/workspace/demo_code//ndk_api_test/app/src/main/jniLibs/${ANDROID_ABI}/libjpeg.so)
      
    4. 鏈接庫

        target_link_libraries( # Specifies the target library.
            ...
             libjpeg
            ...
            )
  1. native 與實現(xiàn)

    1. 聲明: public native void nativeCompress(Bitmap bitmap, int quality, String desPath);

    2. 實現(xiàn)

           extern "C"
       JNIEXPORT void JNICALL
       Java_com_lpb_ndk_1api_1test_MainActivity_nativeCompress(JNIEnv *env, jobject instance, jobject bitmap, jint quality,
                                                               jstring desPath_) {
           /*  *//** 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 info;
           AndroidBitmap_getInfo(env, bitmap, &info);
           //列
           uint32_t col = info.width;
           //行
           uint32_t rows = info.height;
           int32_t format = info.format;
           LOGD("bitmap info =  %d,%d,%d", col, rows, format)
           if (format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
               return;
           }
           u_char *addressPtr;
           AndroidBitmap_lockPixels(env, bitmap, (void **) (&addressPtr));
           u_char *data = static_cast<u_char *>(malloc(col * rows * 3));
           u_char *pointer = data;
           int pixel = 0;
           u_char r, g, b;
           for (int i = 0; i < rows; ++i) {
               for (int i = 0; i < col; ++i) {
                   pixel = *((int *) (addressPtr));
                   r = static_cast<u_char>((pixel & 0x00FF0000) >> 16); // 獲取 R 通道值
                   g = static_cast<u_char>((pixel & 0x0000FF00) >> 8);  // 獲取 G 通道值
                   b = static_cast<u_char>((pixel & 0x000000FF));       // 獲取 B 通道值
                   addressPtr += 4;
                   // 2.2 為 Data 填充數(shù)據(jù)
                   *(data++) = b;
                   *(data++) = g;
                   *(data++) = r;
               }
           }
       
           AndroidBitmap_unlockPixels(env, bitmap);
           char *desPath = const_cast<char *>(env->GetStringUTFChars(desPath_, 0));
           write_JPEG_file(pointer, desPath, rows, col, quality);
           env->ReleaseStringUTFChars(desPath_, desPath);
           free((void *) pointer);
      
    3. 調(diào)用:

       Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ada1);
       File externalCacheDir = getCacheDir();
       nativeCompress(bitmap, 50,externalCacheDir+"/test.jpeg");
      
    4. 注意:

      1. 如需使用 AndroidBitmapInfo 需要在 find_library 中添加 jnigraphics 在鏈接庫的時候加上${jnigraphics}

      2. write_JPEG_file 官方已經(jīng)提供demo 了 https://raw.githubusercontent.com/libjpeg-turbo/libjpeg-turbo/master/example.txt

         #include <stdio.h>
         #include "jpeglib.h"
         #include <setjmp.h>
         
         GLOBAL(void)
         write_JPEG_file(JSAMPLE *image_buffer,char *filename, JDIMENSION rows, JDIMENSION col, int quality) {
             /* This struct contains the JPEG compression parameters and pointers to
              * working space (which is allocated as needed by the JPEG library).
              * It is possible to have several such structures, representing multiple
              * compression/decompression processes, in existence at once.  We refer
              * to any one struct (and its associated working data) as a "JPEG object".
              */
             struct jpeg_compress_struct cinfo;
             /* This struct represents a JPEG error handler.  It is declared separately
              * because applications often want to supply a specialized error handler
              * (see the second half of this file for an example).  But here we just
              * take the easy way out and use the standard error handler, which will
              * print a message on stderr and call exit() if compression fails.
              * Note that this struct must live as long as the main JPEG parameter
              * struct, to avoid dangling-pointer problems.
              */
             struct jpeg_error_mgr jerr;
             /* More stuff */
             FILE *outfile;                /* target file */
             JSAMPROW row_pointer[1];      /* pointer to JSAMPLE row[s] */
             int row_stride;               /* physical row width in image buffer */
         
             /* Step 1: allocate and initialize JPEG compression object */
         
             /* We have to set up the error handler first, in case the initialization
              * step fails.  (Unlikely, but it could happen if you are out of memory.)
              * This routine fills in the contents of struct jerr, and returns jerr's
              * address which we place into the link field in cinfo.
              */
             cinfo.err = jpeg_std_error(&jerr);
             /* Now we can initialize the JPEG compression object. */
             jpeg_create_compress(&cinfo);
         
             /* Step 2: specify data destination (eg, a file) */
             /* Note: steps 2 and 3 can be done in either order. */
         
             /* Here we use the library-supplied code to send compressed data to a
              * stdio stream.  You can also write your own code to do something else.
              * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
              * requires it in order to write binary files.
              */
             if ((outfile = fopen(filename, "wb")) == NULL) {
                 fprintf(stderr, "can't open %s\n", filename);
                 return;
             }
             jpeg_stdio_dest(&cinfo, outfile);
         
             /* Step 3: set parameters for compression */
         
             /* First we supply a description of the input image.
              * Four fields of the cinfo struct must be filled in:
              */
             cinfo.image_width = col;      /* image width and height, in pixels */
             cinfo.image_height = rows;
             cinfo.input_components = 3;           /* # of color components per pixel */
             cinfo.in_color_space = JCS_RGB;       /* colorspace of input image */
             /* Now use the library's routine to set default compression parameters.
              * (You must set at least cinfo.in_color_space before calling this,
              * since the defaults depend on the source color space.)
              */
             jpeg_set_defaults(&cinfo);
             /* Now you can set any non-default parameters you wish to.
              * Here we just illustrate the use of quality (quantization table) scaling:
              */
             jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
         
             /* Step 4: Start compressor */
         
             /* TRUE ensures that we will write a complete interchange-JPEG file.
              * Pass TRUE unless you are very sure of what you're doing.
              */
             jpeg_start_compress(&cinfo, TRUE);
         
             /* Step 5: while (scan lines remain to be written) */
             /*           jpeg_write_scanlines(...); */
         
             /* Here we use the library's state variable cinfo.next_scanline as the
              * loop counter, so that we don't have to keep track ourselves.
              * To keep things simple, we pass one scanline per call; you can pass
              * more if you wish, though.
              */
             row_stride = col * 3; /* JSAMPLEs per row in image_buffer */
         
             while (cinfo.next_scanline < cinfo.image_height) {
                 /* jpeg_write_scanlines expects an array of pointers to scanlines.
                  * Here the array is only one element long, but you could pass
                  * more than one scanline at a time if that's more convenient.
                  */
                 row_pointer[0] = &image_buffer[cinfo.next_scanline * row_stride];
                 (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
             }
         
             /* Step 6: Finish compression */
         
             jpeg_finish_compress(&cinfo);
             /* After finish_compress, we can close the output file. */
             fclose(outfile);
         
             /* Step 7: release JPEG compression object */
         
             /* This is an important step since it will release a good deal of memory. */
             jpeg_destroy_compress(&cinfo);
         
             /* And we're done! */
         }
        

寫在最后的話

最近在學習ndk古涧,后續(xù)會更新相關(guān)的文章上來冰沙。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末侨艾,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子拓挥,更是在濱河造成了極大的恐慌唠梨,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侥啤,死亡現(xiàn)場離奇詭異姻成,居然都是意外死亡,警方通過查閱死者的電腦和手機愿棋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來均牢,“玉大人糠雨,你說我怎么就攤上這事∨枪颍” “怎么了甘邀?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長垮庐。 經(jīng)常有香客問我松邪,道長,這世上最難降的妖魔是什么哨查? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任逗抑,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘邮府。我一直安慰自己荧关,他們只是感情好,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布褂傀。 她就那樣靜靜地躺著忍啤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪仙辟。 梳的紋絲不亂的頭發(fā)上同波,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機與錄音叠国,去河邊找鬼未檩。 笑死,一個胖子當著我的面吹牛煎饼,可吹牛的內(nèi)容都是我干的讹挎。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼吆玖,長吁一口氣:“原來是場噩夢啊……” “哼筒溃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起沾乘,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤怜奖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后翅阵,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體歪玲,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年掷匠,在試婚紗的時候發(fā)現(xiàn)自己被綠了滥崩。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡讹语,死狀恐怖钙皮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情顽决,我是刑警寧澤短条,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站才菠,受9級特大地震影響茸时,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赋访,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一可都、第九天 我趴在偏房一處隱蔽的房頂上張望眯漩。 院中可真熱鬧蔓挖,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽邦邦。三九已至及舍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間芹壕,已是汗流浹背汇四。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留踢涌,地道東北人通孽。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像睁壁,于是被迫代替她去往敵國和親背苦。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 0. 前言 如果只學理論潘明,不做實踐行剂,不踩踩坑,一般很難發(fā)現(xiàn)真正實踐項目中的問題的钳降,也比較難以加深對技術(shù)的理解厚宰。所以...
    LouisLau_6d51閱讀 1,985評論 0 7
  • 先發(fā)一張昨天去看我雷哥演唱會的皂片然后再說正文哈哈。 簡介 由于工作原因遂填,boss下達的任務(wù)就大概說了對圖片進行壓...
    我叫王菜鳥閱讀 5,232評論 2 16
  • 向您的項目添加 C 和 C++ 代碼 本文內(nèi)容 下載 NDK 和構(gòu)建工具 創(chuàng)建支持 C/C++ 的新項目 構(gòu)建和運...
    會飛的大象_閱讀 3,789評論 0 3
  • libwebsockets依賴libuv,libz,mbedtls铲觉,所以構(gòu)建腳本需要提前構(gòu)建以上幾個庫,下面介紹使...
    三萬分之一閱讀 2,450評論 0 1
  • 第一個目標吓坚,通過細分練習撵幽,左手指法,學會礁击,幾個主要和弦的并齐,執(zhí)法。右手學會客税,止痰的手法,練習撕贞,彩虹更耻。30分鐘。 第二...
    Fantasiy閱讀 151評論 0 0