记一次ndk编译jpeg-turbo库中出现的坑人问题,jni图片压缩问题

环境

win7 64位

cmake 3.4.1

Android studio 3.1.3

 

之前看到网上有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也是磕磕碰碰,多看看别人写的代码,确实有一些提升,对比,发现差异,然后就解决不少问题。

你可能感兴趣的:(android开发)