关于为什么使用 Libjpeg 来压缩图片的原因可以看 REASON.md,skia 引擎也是基于 libjpeg 的封装

libjpeg 的 Android jni 库地址:https://github.com/bither/bither-android-lib,可以直接将该库的 jni 文件夹直接拷贝至项目下,通过 cmake 引入构建。

案例

下面介绍的两个库都是基于 bither-android-lib 来实现的。

1、Luban

luban 这个库大家用的还是比较多的,但 luban 是基于 java 层面去压缩的,主要就是对 bitmap 的 options.inSampleSize 进行一个算法计算,具体可以查看源码 computeSize 方法。

而在 luban 的项目中,其实还有另一个通过 libjpeg 来实现的的压缩库,在 luban 的 turbor 分支中,但该分支中只有编译好后的 so 文件和项目使用,具体的源码是在 Luban-Turbo 项目中,我们可以来看下他的项目目录:

image.png


介绍:
1、include 文件夹就是开头指的 bither-android-lib 项目下面的 jni 文件
2、turbo.c 是我们自己写的 jni 接口,通过调用 include 中的 API,计算好结果后然后回调给 java 层
3、CMakeLists.txt 编译 cpp 文件

我们先来看下 CmakeLists.txt(cmake 的具体介绍可以看 cmake 必知):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
cmake_minimum_required(VERSION 3.4.1)
// 指定要编译的库 luban,并将指定 turbo.c 文件
add_library( luban
SHARED
src/main/cpp/turbo.c )

// 将头文件添加到搜索路径中
include_directories(src/main/cpp/include)

// 指定要编译的库 jpeg
add_library( jpeg SHARED IMPORTED )

// 设置库的一些属性
set_target_properties( jpeg
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libjpeg-turbo.a )
// 添加 Android log 库
find_library( log-lib log )

// 将库与其他库相关联
target_link_libraries( luban
jpeg
jnigraphics // 这个地方需要注意一下这个库,这个库是可以直接操作 Android Bitmap 信息的
${log-lib})


然后来看下 turbo.c 的主要片段代码,主要设置步骤如下


1、获取 Bitmap 相关的信息,例如 bitmap 的宽度、高度、像素点颜色值
具体可看代码 nativeCompress 方法的 65~86 行


2、将 Bitmap 中每个像素点的颜色值存储起来:
①、动态分配一块内存空间大小为 bitmap 的宽度高度3,这个 3 指的是 R、G、B

data = malloc((size_t) (width height 3));

②、pixelColor 像素缓存地址的指针,循环遍历 bitmap 的颜色值,并存储到 *data 中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
for (int i = 0; i < height; i++) {
       for (int j = 0; j < width; j++) {
           color = *((int *) pixelColor);
           r = (BYTE) ((color & 0x00FF0000) >> 16);
           g = (BYTE) ((color & 0x0000FF00) >> 8);
           b = (BYTE) (color & 0X000000FF);

           *data = b;
           *(data + 1) = g;
           *(data + 2) = r;
           data += 3;
           pixelColor += 4;
       }
   }

具体可看代码 nativeCompress 方法的 88~106 行


3、通过 libjpeg API 来压缩 *data 数据,并存储到一个文件中
具体代码可以查看 generateJpg 方法的 18~59 行 ,这个地方不再去阐述的原因是,在参考的很多项目中,这个部分的代码几乎都是一模一样,这个地方需要注意一下 jcs.optimize_coding = TRUE; ,由于设置成 true,给压缩的质量提高了 5~10 倍的效果,具体可以看开题的 REASON.md


2、EffectiveBitmap

EffectiveBitmap 几乎和 Luban-Turbo 一模一样,只是代码变量换了一下而已,看了一下两个库的构建时间

  • luban-turbo : 2018-7-20 ,具体可看 commit
  • EffectiveBitmap:2018-4-12 ,具体可看 commit

说代码差不多雷同其实也没什么,主要是因为官方给过一份 turbo 的 example,人家就是这么写的,我们就是这么调用的,一样的能怪我嘛!

总结

libjpeg 仅支持将压缩的结果设置到一个文件中,不能返回一个 bitmap ,如果压缩的目标是一个 File 文件,可以将文件先 BitmapFactory.decodeFile(filePath, options); 转成 bitmap,然后调用 native 方法,这个是从上述的两个案例中参考得出的,官方还提供了解压缩的方法,具体看以看 example 中 read_JPEG_file 函数。

参考文章: