参考了这篇总结https://juejin.im/post/5cb1d6f7518825186d653aa7,在此基础上加入了一些遇到的bug以及解决方法
编译环境
1. Ubuntu-18.04.1
2. ndk 13
3. libjpeg-turbo 最新源码
4. cmake 3.12.1
编译前准备工作
- 下载ndk
- 下载cmake
- 下载源码
- 修改配置
-
打开 libjpeg-turbo/sharedLibs/CMakeList.txt, 将设置版本号的位置注释, 否则在使用时, 可能会出现运行时缺少 so 库的问题
-
编译脚本
需要改动的地方就是 #1 #2 #3 替换成本地的环境
执行脚本
-
编译后so库位置
#!/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环境配置
-
拷贝头文件和so库到对应的位置
- so库 :main/jniLibs/armeabi/
- 头文件:cpp/include/
-
配置CMakeLists.txt
-
引入头文件
include_directories( ${CMAKE_SOURCE_DIR}/include)
-
添加库名字
add_library( libjpeg SHARED IMPORTED)
-
设置库的属性
set_target_properties( libjpeg PROPERTIES IMPORTED_LOCATION //路径需要写绝对路径 C:/workspace/demo_code//ndk_api_test/app/src/main/jniLibs/${ANDROID_ABI}/libjpeg.so)
链接库
-
target_link_libraries( # Specifies the target library.
...
libjpeg
...
)
-
native 与实现
声明: public native void nativeCompress(Bitmap bitmap, int quality, String desPath);
-
实现
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); -
调用:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ada1); File externalCacheDir = getCacheDir(); nativeCompress(bitmap, 50,externalCacheDir+"/test.jpeg");
-
注意:
如需使用 AndroidBitmapInfo 需要在 find_library 中添加 jnigraphics 在链接库的时候加上${jnigraphics}
-
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,后续会更新相关的文章上来。