Android圖片壓縮加密上傳 NDK終極壓縮和加密上傳

###1. 概述android


上一期已講到Android圖片壓縮加密上傳 - JPEG壓縮算法解析,咱們不打算採用BitmapFactory去壓縮,而是採用JPEG的壓縮算法,固然你們最好是將二者結合一下,今天咱們直接去網上找一個已經寫好的開源庫,而後咱們在他的基礎上再寫一些Native代碼就好,固然也能夠本身一步一步去寫算法處理。git

效果演示
全部分享大綱: 2017Android進階之路與你同行

視頻講解地址:http://pan.baidu.com/s/1eR8ZnxS    ###2. 編譯libjpeg.so庫文件github


關於C和C++以及NDK的基礎我這裏就不強調,若是你們以爲我寫的C++代碼看不懂那麼也不要緊,你只須要關注我這裏所實現的思路,我最終把它編譯成.so庫你能用就行。   打開https://github.com/libjpeg-turbo/libjpeg-turbo 下載已經提供好的開源庫,打開後你會看到有不少的.c文件和.h頭文件,把他編譯成.so庫文件便可,固然道路曲折會出現不少問題,這裏我就不寫過程了,等到增量更新的時候我會一步一步去帶你們編譯的。   咱們將已經編譯好的libjpeg.so以及.h頭文件拷貝到jni目錄下面,就能夠開始寫壓縮代碼了:算法

目前jni目錄

###3. 編寫imgcompcrypt.cpp數組


我使用的是AS,網上關於AS的NDK方面的資料比較少,須要多花一些功夫,最好把AS升級到2.2版本以上,而後下載NDK、CMake、LLDB,一個支持、一個插件、一個調試:bash

{4U3J{YTF`%)}1T4)W6LUH1.png

升級到2.2以後咱們寫C和C++的代碼纔會有提示,以前的版本我還沒找到解決的方案,網上搜索了不少也沒有相關答案,並且它也支持Cmake和ndk-build兩種方式,相比與之前的gradle去配置ndk編譯目錄什麼的簡直是方便多了。對於老的經過Android.mk文件編譯的NDK項目,直接一條配置整個項目就能夠被AS支持了。若是以爲Cmake的方式不習慣仍是能夠採用ndk-build的方式這點卻是無所謂,至於代碼提示確定是要的這個很致命,要否則寫代碼會比較慢。怎麼生成頭文件我就不講了,這裏直接上代碼:markdown

#include "imgcompcrypt.h"
#include <string.h>
#include <android/bitmap.h>
#include <android/log.h>
#include <stdio.h>
#include <setjmp.h>
#include <math.h>
#include <stdint.h>
#include <time.h>



//統一編譯方式
extern "C" {
#include "jpeg/jpeglib.h"
#include "jpeg/cdjpeg.h" /* Common decls for cjpeg/djpeg applications */
#include "jpeg/jversion.h" /* for version message */
#include "jpeg/jconfig.h"
#include "filecrypt.c"
}

// log打印
#define LOG_TAG "jni"
#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;

// error 結構體
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;
    (*cinfo->err->output_message)(cinfo);
    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]);
    longjmp(myerr->setjmp_buffer, 1);
}

int generateJPEG(BYTE *data, int w, int h, int quality,
                 const char *outfilename, jboolean optimize) {

    // 結構體至關於Java類
    struct jpeg_compress_struct jcs;

    //當讀完整個文件的時候就會回調my_error_exit這個退出方法。
    struct my_error_mgr jem;
    jcs.err = jpeg_std_error(&jem.pub);
    jem.pub.error_exit = my_error_exit;
    // setjmp是一個系統級函數,是一個回調。
    if (setjmp(jem.setjmp_buffer)) {
        return 0;
    }

    //初始化jsc結構體
    jpeg_create_compress(&jcs);
    //打開輸出文件 wb 可寫  rb 可讀
    FILE *f = fopen(outfilename, "wb");
    if (f == NULL) {
        return 0;
    }
    //設置結構體的文件路徑,以及寬高
    jpeg_stdio_dest(&jcs, f);
    jcs.image_width = w;
    jcs.image_height = h;

    // /* TRUE=arithmetic coding, FALSE=Huffman */
    jcs.arith_code = false;
    int nComponent = 3;
    /* 顏色的組成 rgb,三個 # of color components in input image */
    jcs.input_components = nComponent;
    //設置顏色空間爲rgb
    jcs.in_color_space = JCS_RGB;
    ///* Default parameter setup for compression */
    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;
}

jint Java_net_bither_util_NativeUtil_compressBitmap(JNIEnv *env,
                                                    jclass thiz, jobject bitmap, int quality,
                                                    jstring fileNameStr, jboolean optimize) {
    // 1.獲取Bitmap信息
    AndroidBitmapInfo android_bitmap_info;
    AndroidBitmap_getInfo(env, bitmap, &android_bitmap_info);
    // 獲取bitmap的 寬,高,format
    int bitmap_width = android_bitmap_info.width;
    int bitmap_height = android_bitmap_info.height;
    int format = android_bitmap_info.format;

    if (format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
        return -1;
    }
    // 2.解析Bitmap的像素信息,並轉換成RGB數據,保存到二維byte數組裏面
    BYTE *pixelscolor;
    // 2.1 鎖定畫布
    AndroidBitmap_lockPixels(env, bitmap, (void **) &pixelscolor);
    // 2.2 解析初始化參數值
    BYTE *data;
    BYTE r, g, b;
    data = (BYTE *) malloc(bitmap_width * bitmap_height * 3);//每個像素都有三個信息RGB
    BYTE *tmpData;
    tmpData = data;//臨時保存data的首地址
    int i = 0, j = 0;
    int color;
    //2.3 解析每個像素點裏面的rgb值(去掉alpha值),保存到一維數組data裏面
    for (i = 0; i < bitmap_height; ++i) {
        for (j = 0; j < bitmap_width; ++j) {
            //獲取二維數組的每個像素信息首地址
            color = *((int *) pixelscolor);
            r = ((color & 0x00FF0000) >> 16);
            g = ((color & 0x0000FF00) >> 8);
            b = ((color & 0x000000FF));
            //保存到data數據裏面
            *data = b;
            *(data + 1) = g;
            *(data + 2) = r;
            data = data + 3;
            // 一個像素包括argb四個值,每+4就是取下一個像素點
            pixelscolor += 4;
        }
    }
    // 2.4. 解鎖Bitmap
    AndroidBitmap_unlockPixels(env, bitmap);
    // jstring --> c char
    char *fileName = (char*)(env)->GetStringUTFChars(fileNameStr, 0);

    //3. 調用libjpeg核心方法實現壓縮
    int resultCode = generateJPEG(tmpData, bitmap_width, bitmap_height, quality, fileName, optimize);

    //4.釋放資源
    env->ReleaseStringUTFChars(fileNameStr, fileName);
    free((void *) tmpData);
    // 4.2 釋放Bitmap
    // 4.2.1 經過對象獲取類
    jclass bitmap_clz = env->GetObjectClass(bitmap);
    // 4.2.2 經過類和方法簽名獲取方法id
    jmethodID recycle_mid = env->GetMethodID(bitmap_clz, "recycle", "()V");
    // 4.2.3 執行回收釋放方法
    env->CallVoidMethod(bitmap, recycle_mid);

    // 5.返回結果
    if (resultCode == 0) {
        return -1;
    }
    return 1;
}
複製代碼

###3. 圖片文件加密app


文件的加密相對來講就比較簡單了,由於可能可不少地方涉及到文件加密,這裏我就把文件加密單獨分開了,固然也能夠寫到圖片壓縮一塊兒,能夠用C寫也能夠用C++由於上面是用的C++,那麼加密咱們就採用C:ide

#include <jni.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "net_bither_util_FileCrypt.h"

// 加密的祕鑰
char password[] = "Big god take me fly!";

// 加密文件
void crypt_file(char *normal_path, char *crypt_path) {
    //打開文件
    FILE *normal_fp = fopen(normal_path, "rb");
    FILE *crypt_fp = fopen(crypt_path, "wb");
    //一次讀取一個字符
    int ch;
    int i = 0; //循環使用密碼中的字母進行異或運算
    int pwd_len = strlen(password); //密碼的長度
    while ((ch = fgetc(normal_fp)) != EOF) { //End of File
        //寫入(異或運算)
        fputc(ch ^ password[i % pwd_len], crypt_fp);
        i++;
    }
    // 關閉
    fclose(crypt_fp);
    fclose(normal_fp);
}

// 加密文件,jfile_path 源文件路徑  jcrypt_path 加密後文件路徑
JNIEXPORT void JNICALL Java_com_hc_filecrypt_FileCrypt_cryptFile
        (JNIEnv *env, jclass jclazz, jstring jfile_path, jstring jcrypt_path) {
    char *normal_path = (char*)(*env)->GetStringUTFChars(env, jfile_path, JNI_FALSE);
    char *crypt_path = (char*)(*env)->GetStringUTFChars(env, jcrypt_path, JNI_FALSE);
    crypt_file(normal_path, crypt_path);
}
複製代碼

###4. 最後的測試函數


接近3M的原圖壓縮到30K,能夠找找哪一張是被壓縮過的,會比咱們使用BitmapFarctory或者Bitmap.compress()壓縮出來的一些效果要好不少:

效果對比

全部分享大綱:2017Android進階之路與你同行

視頻講解地址:http://pan.baidu.com/s/1eR8ZnxS

相關文章
相關標籤/搜索