原文连接:blog.csdn.net/u014011112/…html
项目连接:github.com/zengfw/Effe…android
直接使用项目或直接复制libs中的so库到项目中便可(当前只构建了armeabi),须要其余ABI可检下项目另外使用CMake构建便可。git
结果预览:github
jni_278KB.png 算法
quality_484KB.png bash
sample_199KB.png ide
size_238KB.png 测试
原图大小5.99M~~ 咱们把全部通过压缩的图片放到同等大小的状况后,很明显,采样压缩跟尺寸压缩都不是咱们想要的结果,而质量压缩跟JNI压缩我设置的质量压缩值都是30,JNI压缩出来只有278KB,直接质量压缩出来的有484KB,综合以后,JNI才是综合最优的方式,固然,若是只是头像,咱们设置能够把配置值设置得更小,图片就更小。gradle
首先了解两个图像处理库:libjpeg、Skia。ui
Skia:图像处理引擎,Google在Android系统上就是采用Skia,它是基于libjpeg的二次封装,Google在不少其它产品也使用了这个库,好比Chorme,Firefox等等。
libjpeg:早期的图像处理引擎,用于PC端。
官方文档能够看到libjpeg.doc这样一段话:
boolean optimize_coding
TRUE causes the compressor to compute optimal Huffman coding tables
for the image. This requires an extra pass over the data and
therefore costs a good deal of space and time. The default is
FALSE, which tells the compressor to use the supplied or default
Huffman tables. In most cases optimal tables save only a few percent
of file size compared to the default tables. Note that when this is
TRUE, you need not supply Huffman tables at all, and any you do
supply will be overwritten.
复制代码
能够先了解下什么是哈夫曼树和哈夫曼编码
看完博客以后,能够知道最优哈夫曼编码实际上是使用了可变长编码方式,而默认的哈夫曼编码使用了定长编码方式,所以须要更多的存储空间,呈现出来的手机图片天然会大很大。
libjpeg把optimize_coding参数默认设置为FALSE是由于10多年前的Android手机CPU跟内存都很是吃紧,因此当年没有设置为TRUE。现在的手机CPU跟内存都“起飞了”,Goolge的Skia图像处理引擎却仍是使用optimize_coding的默认值FALSE。咱们没法修改系统Skia的这个参数值,因此只能默默忍受size很大的图像文件。
1.图片压缩到相同的质量,FALSE所产出的图像文件大小是TRUE的5-10倍。 2.图片压缩到相同的质量,Android所产出的图像文件大小比iOS也是大5-10倍。
因此,经过使用libjpeg编译本身的native library修改optimize_coding参数的值,达图像质量相同,所产出的图像却能节省5-10倍空间大小的效果。
1.构建libjpeg的so库 到官方下载对应本身电脑系统类型的压缩包,建立Android项目导入压缩包里头的xx.h、xx.c文件构建so库。bither/bither-android-lib已经作了这个工做,所以咱们只需直接拿他的libjpegbither.so便可。
2.导入libjpeg的声明头文件,由于步骤1的libjpegbither.so是对这些头文件的实现,所以须要导入这些头文件。
3.建立CMake脚本
cmake_minimum_required(VERSION 3.4.1)
add_library( effective-bitmap
SHARED
src/main/cpp/effective-bitmap.c )
include_directories( src/main/cpp/jpeg/
)
add_library(jpegbither SHARED IMPORTED)
set_target_properties(jpegbither
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libjpegbither.so)
find_library( log-lib
log )
find_library( jnigraphics-lib jnigraphics )
target_link_libraries( effective-bitmap
jpegbither
${log-lib}
${jnigraphics-lib})
复制代码
4.配置gradle关联CMakeLists.txt构建脚本,以及指定产出的ABI的类型
android {
// ...
defaultConfig {
// ...
externalNativeBuild {
cmake {
cppFlags ""
}
}
ndk {
abiFilters 'armeabi'
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
复制代码
5.核心C/C++代码
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;
}
复制代码
6.构建so库
参考连接: Why the image quality of iPhone is much better than Android?