###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