之前看到网上有jni方式压缩图片,就看了一下,参照别人的代码进行了实验,但是结果怎么也无法编译通过。
参照博客:https://blog.csdn.net/hqiong208/article/details/53667661
使用CMakeLists文件方式编译,突然觉得这个比Android.mk文件编写起来简单一些,少了一堆
include $(CLEAR_VARS);
... include $(BUILD_SHARED_LIBRARY);
替代之后直接使用
add_library( # Sets the name of the library. [modulename] # Sets the library as a shared library. [moduletype (shared|static)] # Provides a relative path to your source file(s) [src_files] )
好了,不说废话了。这里列出了CMakeLists.txt中一些关键性的代码。
#配置jpeg.so 文件路径,此路径为绝对路径,经测试,相对路径不能编译通过,添加其他第三方库的时候也应该注意,${CMAKE_SOURCE_DIR}代表的是CMakeLists.txt所在目录绝对路径 set(lib_src_DIR ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}) #导入编译好的jpeg-turbo库 add_library(jpeg-and SHARED IMPORTED ) #这句话是jpeg对应的so文件,so文件是放到ibs这个文件夹中(相对与cpp这个文件的位置) set_target_properties(jpeg-and PROPERTIES IMPORTED_LOCATION ${lib_src_DIR}/libjpeg.so) #添加android log库 find_library( # Sets the name of the path variable. log-lib # Specifies the name of the NDK library that # you want CMake to locate. log ) #添加jnigraphics,c++代码中使用到了AndroidBitmap,所以需要添加jnigrapics库 find_library( # Sets the name of the path variable. andbitmap jnigraphics ) add_library(zjybmcompress SHARED src/main/cpp/JpegCompress.cpp) target_link_libraries(zjybmcompress ${andbitmap} jpeg-and ${log-lib})
编写完成,build,但是不知道哪里出问题了,就是报错,错误内容如下:
error: undefined reference to 'jpeg_std_error(jpeg_error_mgr*)'
error: undefined reference to 'jpeg_CreateCompress(jpeg_compress_struct*, int, unsigned int)'
error: undefined reference to 'jpeg_stdio_dest(jpeg_compress_struct*, __sFILE*)'
error: undefined reference to 'jpeg_set_defaults(jpeg_compress_struct*)'
error: undefined reference to 'jpeg_set_quality(jpeg_compress_struct*, int, int)'
error: undefined reference to 'jpeg_start_compress(jpeg_compress_struct*, int)'
error: undefined reference to 'jpeg_write_scanlines(jpeg_compress_struct*, unsigned char**, unsigned int)'
error: undefined reference to 'jpeg_finish_compress(jpeg_compress_struct*)'
error: undefined reference to 'jpeg_destroy_compress(jpeg_compress_struct*)'
当你看到这些错误的时候,没错,你和我一样,碰上了最大的坑
几经查找,总算找到一种可以解决的办法:
项目中引用到的头文件需要使用 extern “C”{}包含起来
参考https://www.cnblogs.com/whisht/archive/2012/12/25/3085071.html
总结:
自己后来一想,确实也是,libjpeg-turbo库里面的源文件都是c文件,不是cpp文件,作者的代码里面也是c文件,而我的代码里面使用的C++写的。真是被c和c++兼容性问题被坑惨了,以后写代码一定要看清,别人的库用的c写的还是c++写的。
所以直接拷贝作者的代码会有一些问题,这边贴上自己的代码。
#include#include #include #include #include #include #include #include #include extern "C" { #include "jpeg/jpeglib.h" #include "jpeg/jversion.h" #include "jpeg/android/config.h" #include "jpeg/cdjpeg.h" } #define LOG_TAG "------jni_jpegCompress----" //#define LOGW(...) __android_log_write(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__) //#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) #define true 1 #define false 0 typedef uint8_t BYTE; char *error; struct my_error_mgr { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer; }; typedef struct my_error_mgr *my_error_ptr; METHODDEF(void) my_error_exit(j_common_ptr cinfo) { my_error_ptr myerr = (my_error_ptr) cinfo->err; error = (char *) myerr->pub.jpeg_message_table[myerr->pub.msg_code]; LOGE("jpeg_message_table[%d]:%s", myerr->pub.msg_code, myerr->pub.jpeg_message_table[myerr->pub.msg_code]); // LOGE("addon_message_table:%s", myerr->pub.addon_message_table); // LOGE("SIZEOF:%d",myerr->pub.msg_parm.i[0]); // LOGE("sizeof:%d",myerr->pub.msg_parm.i[1]); longjmp(myerr->setjmp_buffer, 1); } int generateJPEG(BYTE *data, int w, int h, int quality, const char *outfilename, jboolean optimize) { int nComponent = 3; // jpeg的结构体,保存的比如宽、高、位深、图片格式等信息 struct jpeg_compress_struct jcs; struct my_error_mgr jem; jcs.err = jpeg_std_error(&jem.pub); jem.pub.error_exit = my_error_exit; if (setjmp(jem.setjmp_buffer)) { return 0; } jpeg_create_compress(&jcs); // 打开输出文件 wb:可写byte FILE *f = fopen(outfilename, "wb"); if (f == NULL) { return 0; } // 设置结构体的文件路径 jpeg_stdio_dest(&jcs, f); jcs.image_width = w; jcs.image_height = h; // 设置哈夫曼编码 jcs.arith_code = false; jcs.input_components = nComponent; if (nComponent == 1) jcs.in_color_space = JCS_GRAYSCALE; else jcs.in_color_space = JCS_RGB; jpeg_set_defaults(&jcs); jcs.optimize_coding = optimize; jpeg_set_quality(&jcs, quality, true); // 开始压缩,写入全部像素 jpeg_start_compress(&jcs, TRUE); JSAMPROW row_pointer[1]; int row_stride; row_stride = jcs.image_width * nComponent; while (jcs.next_scanline < jcs.image_height) { row_pointer[0] = &data[jcs.next_scanline * row_stride]; jpeg_write_scanlines(&jcs, row_pointer, 1); } jpeg_finish_compress(&jcs); jpeg_destroy_compress(&jcs); fclose(f); return 1; } typedef struct { uint8_t r; uint8_t g; uint8_t b; } rgb; char *jstrinTostring(JNIEnv *env, jbyteArray barr) { char *rtn = NULL; jsize alen = env->GetArrayLength(barr); jbyte *ba = env->GetByteArrayElements(barr, NULL); if (alen > 0) { rtn = (char *) malloc(alen + 1); memcpy(rtn, ba, alen); rtn[alen] = 0; } env->ReleaseByteArrayElements(barr, ba, 0); return rtn; } extern "C" jstring Java_com_zjy_bitmap_utils_EffectiveBitmapUtils_compressBitmap(JNIEnv *env, jobject thiz, jobject bitmapcolor, int w, int h, int quality, jbyteArray fileNameStr, jboolean optimize) { AndroidBitmapInfo infocolor; BYTE *pixelscolor; int ret; BYTE *data; BYTE *tmpdata; char *fileName = jstrinTostring(env, fileNameStr); if ((ret = AndroidBitmap_getInfo(env, bitmapcolor, &infocolor)) < 0) { LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret); return env->NewStringUTF("0");; } if ((ret = AndroidBitmap_lockPixels(env, bitmapcolor, (void **) &pixelscolor)) < 0) { LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret); } BYTE r, g, b; data = NULL; data = (BYTE *) (malloc(w * h * 3)); tmpdata = data; int j = 0, i = 0; int color; for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { color = *((int *) pixelscolor); r = ((color & 0x00FF0000) >> 16); g = ((color & 0x0000FF00) >> 8); b = color & 0x000000FF; *data = b; *(data + 1) = g; *(data + 2) = r; data = data + 3; pixelscolor += 4; } } AndroidBitmap_unlockPixels(env, bitmapcolor); int resultCode = generateJPEG(tmpdata, w, h, quality, fileName, optimize); free(tmpdata); if (resultCode == 0) { jstring result = env->NewStringUTF(error); error = NULL; return result; } return env->NewStringUTF("1"); //success }
当然,你拷贝我的代码也可能会存在头文件路径问题,但是代码里面不会有什么错误,已经完全改为c++文件的方式并且编译通过。
费了好几天,总算解决了,之前ndk下使用opencv的时候也遇到了一个问题,就是最开头我提到的一个绝对路径问题,太坑太坑,害的我用的Android.mk方式编译的,其实也是因为我对CMakeLists.txt文件编写的经验不足,其实我写Android.mk也是磕磕碰碰,多看看别人写的代码,确实有一些提升,对比,发现差异,然后就解决不少问题。