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<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 填充数据
                   *(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. 调用:

       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 <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,后续会更新相关的文章上来。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容

  • 0. 前言 如果只学理论,不做实践,不踩踩坑,一般很难发现真正实践项目中的问题的,也比较难以加深对技术的理解。所以...
    LouisLau_6d51阅读 1,981评论 0 7
  • 先发一张昨天去看我雷哥演唱会的皂片然后再说正文哈哈。 简介 由于工作原因,boss下达的任务就大概说了对图片进行压...
    我叫王菜鸟阅读 5,228评论 2 16
  • 向您的项目添加 C 和 C++ 代码 本文内容 下载 NDK 和构建工具 创建支持 C/C++ 的新项目 构建和运...
    会飞的大象_阅读 3,779评论 0 3
  • libwebsockets依赖libuv,libz,mbedtls,所以构建脚本需要提前构建以上几个库,下面介绍使...
    三万分之一阅读 2,435评论 0 1
  • 第一个目标,通过细分练习,左手指法,学会,几个主要和弦的,执法。右手学会,止痰的手法,练习,彩虹。30分钟。 第二...
    Fantasiy阅读 149评论 0 0