linux下编译libjpeg-turbo Android中使用libjpeg

参考了这篇总结https://juejin.im/post/5cb1d6f7518825186d653aa7,在此基础上加入了一些遇到的bug以及解决方法

编译环境

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, 将设置版本号的位置注释, 否则在使用时, 可能会出现运行时缺少 so 库的问题


      16a16af9e818ef73.png

编译脚本

  1. 需要改动的地方就是 #1 #2 #3 替换成本地的环境

  2. 执行脚本

  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
     # 编译的过程中产生的中间件的存放目录,为了区分编译目录,源码目录,install目录
     MY_BUILD_DIR=binary
     
     ##  CMake 环境变量    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}   #创建当前arch_abi的编译目录,比如:binary/armeabi-v7a
         cd ${BUILD_DIR}         #此处 进了当前arch_abi的2级编译目录
     
     # 运行时创建临时编译链文件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编译目录跳出,对应上面的cd ${BUILD_DIR},以便function多次执行
         cd ../../
     
         echo "-------------------$2 build end-------------------------"
     }
     
     # build armeabi 编译armeabi
     build_bin arm armeabi arm-linux-androideabi arm-linux-androideabi "$ANDROID_ARMV5_CFLAGS" arm
    

Android环境配置

  1. 拷贝头文件和so库到对应的位置

    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. 设置库的属性

        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 与实现

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

    2. 实现

           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(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((pixel & 0x00FF0000) >> 16); // 获取 R 通道值
                   g = static_cast((pixel & 0x0000FF00) >> 8);  // 获取 G 通道值
                   b = static_cast((pixel & 0x000000FF));       // 获取 B 通道值
                   addressPtr += 4;
                   // 2.2 为 Data 填充数据
                   *(data++) = b;
                   *(data++) = g;
                   *(data++) = r;
               }
           }
       
           AndroidBitmap_unlockPixels(env, bitmap);
           char *desPath = const_cast(env->GetStringUTFChars(desPath_, 0));
           write_JPEG_file(pointer, desPath, rows, col, quality);
           env->ReleaseStringUTFChars(desPath_, desPath);
           free((void *) pointer);
      
    3. 调用:

       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 官方已经提供demo 了 https://raw.githubusercontent.com/libjpeg-turbo/libjpeg-turbo/master/example.txt

         #include 
         #include "jpeglib.h"
         #include 
         
         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,后续会更新相关的文章上来。

你可能感兴趣的:(linux下编译libjpeg-turbo Android中使用libjpeg)