###1. 概述android
上一期已講到Android圖片壓縮加密上傳 - JPEG壓縮算法解析,咱們不打算採用BitmapFactory去壓縮,而是採用JPEG的壓縮算法,固然你們最好是將二者結合一下,今天咱們直接去網上找一個已經寫好的開源庫,而後咱們在他的基礎上再寫一些Native代碼就好,固然也能夠本身一步一步去寫算法處理。git
視頻講解地址: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目錄下面,就能夠開始寫壓縮代碼了:算法
###3. 編寫imgcompcrypt.cpp數組
我使用的是AS,網上關於AS的NDK方面的資料比較少,須要多花一些功夫,最好把AS升級到2.2版本以上,而後下載NDK、CMake、LLDB,一個支持、一個插件、一個調試:bash
升級到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