在 Android 中进行图片压缩是非常常见的开发场景,主要的压缩方法有两种:其一是质量压缩,其二是下采样压缩。
前者是在不改变图片尺寸的情况下,改变图片的存储体积,而后者则是降低图像尺寸,达到相同目的。
质量压缩逻辑
在Android中,对图片进行质量压缩,通常我们的实现方式如下所示:
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//quality 为0~100,0表示最小体积,100表示最高质量,对应体积也是最大
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
在上述代码中,我们选择的压缩格式是CompressFormat.JPEG,除此之外还有两个选择:
- CompressFormat.PNG:PNG 格式是无损的,它无法再进行质量压缩,quality 这个参数就没有作用了,会被忽略,所以最后图片保存成的文件大小不会有变化;
- CompressFormat.WEBP:这个格式是 google 推出的图片格式,它会比 JPEG 更加省空间,经过实测大概可以优化 30% 左右
将 PNG 图片转成 JPEG 格式之后不会降低这个图片的尺寸,但是会降低视觉质量,从而降低存储体积。同时,由于尺寸不变,所以将这个图片解码成相同色彩模式的 bitmap 之后,占用的内存大小和压缩前是一样的。
函数 compress 经过一连串的 java 层调用之后,最后来到了一个 native 函数,如下:
//Bitmap.cpp
static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle,
jint format, jint quality,
jobject jstream, jbyteArray jstorage) {
LocalScopedBitmap bitmap(bitmapHandle);
SkImageEncoder::Type fm;
switch (format) {
case kJPEG_JavaEncodeFormat:
fm = SkImageEncoder::kJPEG_Type;
break;
case kPNG_JavaEncodeFormat:
fm = SkImageEncoder::kPNG_Type;
break;
case kWEBP_JavaEncodeFormat:
fm = SkImageEncoder::kWEBP_Type;
break;
default:
return JNI_FALSE;
}
if (!bitmap.valid()) {
return JNI_FALSE;
}
bool success = false;
std::unique_ptr<SkWStream> strm(CreateJavaOutputStreamAdaptor(env, jstream, jstorage));
if (!strm.get()) {
return JNI_FALSE;
}
std::unique_ptr<SkImageEncoder> encoder(SkImageEncoder::Create(fm));
if (encoder.get()) {
SkBitmap skbitmap;
bitmap->getSkBitmap(&skbitmap);
success = encoder->encodeStream