本文目录
一、前言
上一篇我们编译出了libjpeg库,本片我们主要用libjpeg来对图片进行质量压缩,既然提到图片质量压缩那我们就详细梳理一下安卓图片质量压缩方面的知识,另外我们还要搞懂安卓本身已经提供了对图片质量压缩的接口为什么我们还要使用libjpeg呢?
本片同样比较简单,如果没有C/C++基础可能有一点难度,好了,进入本篇正题。
二、图片质量压缩
安卓常见图片压缩分为两种:质量压缩与下采样压缩(也可称为尺寸压缩)。质量压缩就是不改变图片尺寸,降低图片质量来减少图片存储体积,二下采样压缩降低图片尺寸来达到同样目的。
对于图片质量压缩核心逻辑如下:
1 /**
2 * 对图片进行质量压缩
3 * @param bitmap 待压缩图片
4 * @param format 压缩的格式
5 * @param q 质量
6 * @param path 文件地址
7 */
8 private void qualityCompress(Bitmap bitmap, Bitmap.CompressFormat format,
9 int q, String path) {
10 FileOutputStream fos = null;
11 try {
12 fos = new FileOutputStream(path);
13 bitmap.compress(format, q, fos);
14 } catch (FileNotFoundException e) {
15 e.printStackTrace();
16 } finally {
17 if (null != fos) {
18 try {
19 fos.close();
20 } catch (IOException e) {
21 e.printStackTrace();
22 }
23 }
24 }
25 }
核心就是调用Bitmap的compress方法,其中format参数有三种取值:
1 CompressFormat.PNG:将图片压缩成png格式图片,由于PNG是无损压缩格式,所以其无法在进行质量压缩,并且如果将jpg格式图片压缩成png格式其体积还会变大。
2 CompressFormat.WEBP:webp格式是谷歌推出的一种图片格式,比jpg格式图片更加节省空间,平均来说能节省30%左右。
3 CompressFormat.JPEG:图片压缩成jpg格式,jpg格式可以进行质量压缩。
我们自己项目统一采用jpg格式图片上传,本篇也主要围绕jpg格式图片来展开。
我们简要看一下Bitmap的compress方法源码逻辑:
1 public boolean compress(CompressFormat format, int quality, OutputStream stream) {
2 checkRecycled("Can't compress a recycled bitmap");
3 // do explicit check before calling the native method
4 if (stream == null) {
5 throw new NullPointerException();
6 }
7 if (quality < 0 || quality > 100) {
8 throw new IllegalArgumentException("quality must be 0..100");
9 }
10 StrictMode.noteSlowCall("Compression of a bitmap is slow");
11 Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "Bitmap.compress");
12 boolean result = nativeCompress(mNativePtr, format.nativeInt,
13 quality, stream, new byte[WORKING_COMPRESS_STORAGE]);
14 Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
15 return result;
16 }
最核心就是调用了nativeCompress方法,此方法为native方法,最终通过jni调用Bitmap.cpp中的Bitmap_compress方法:
1static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle,
2 jint format, jint quality,
3 jobject jstream, jbyteArray jstorage) {
4 SkEncodedImageFormat fm;
5 switch (format) {
6 case kJPEG_JavaEncodeFormat:
7 fm = SkEncodedImageFormat::kJPEG;
8 break;
9 case kPNG_JavaEncodeFormat:
10 fm = SkEncodedImageFormat::kPNG;
11 break;
12 case kWEBP_JavaEncodeFormat:
13 fm = SkEncodedImageFormat::kWEBP;
14 break;
15 default:
16 return JNI_FALSE;
17 }
18
19 LocalScopedBitmap bitmap(bitmapHandle);
20 if (!bitmap.valid()) {
21 return JNI_FAL