Android Bitmap压缩的坑
- 微信小程序分享时,微信要求的分享小程序大小有限制,印象中是图片的大小不能超过128KB。
- 所以在过程中针对性地将bitmap进行了压缩。没想到遇到了PNG压缩的一个小坑。
- 特此记录下。
Bitmap压缩
- Bitmap最终是要压缩成byte[]进行分享,而我在开发中,面对到的图片大小不一。
- 大的时候,能到好几MB,小的怎么也几百KB。这年头128KB大小的图片限制,其实挺不科学的。5G普及以后感觉图片会越来越大的。
- 所以,在分享小程序之前,我需要先把Bitmap的大小进行压缩。
压缩方案:
- 我当时的想法是,每一次将Bitmap的大小,压缩为本身质量的90%,这样一步一步地往下缩放,缩放到合适的大小为止。
- 使用的API,就是Bitmap本身的compress()函数:
public boolean compress(CompressFormat format, int quality, OutputStream stream) ;
- 这个函数也不复杂
- CompressFormat :压缩格式,传入个PNG、JPEG之类的(坑就在这里)
- quality:quality就更简单了,0-100,传入一个值,就是压缩的百分比
- OutputStream:这个是最终压缩返回的流,压缩后的Bitmap数据放到传入的流中。
坑
- 坑就坑在传入的CompressFormat上。
- 大家都知道,其实常用的图片格式,在Android或者Web端,并不是JPEG,而是PNG。
- 至少我经手的所有项目里,resources用png的概率真是太大了,毕竟小一些。
- 当然google还搞了一个webp的格式,这玩儿比较搞笑,IOS好像不支持,因为我用这个格式尝试过分享给IOS端,在IOS端的微信上看不到这个图片。
- 言归正传,我在使用compress()的时候,传入的CompressFormat格式,一开始是PNG。
Bitmap tmp = 来一个Bitmap对象;
ByteArrayOutputStream output = new ByteArrayOutputStream();
tmp.compress(Bitmap.CompressFormat.PNG, 90, output);
- 然后Rock'N'Roll吧,压缩压缩压缩…………
- 搞了半天,特么PNG走compress和没走compress效果一样的。传入的quality 0-100,随便你怎么填,只要是PNG格式,来的时候是多大,走的时候是多大。
- 真真的我挥一挥衣袖,不带走一片云彩啊!!!
- 当时我也是一脸懵逼,不是说好了compress就compress呢,怎么你PNG就静悄悄啥也不做???
##PNG是lossless
- 后来,看了下compress的源码:
/**
* Write a compressed version of the bitmap to the specified outputstream.
* If this returns true, the bitmap can be reconstructed by passing a
* corresponding inputstream to BitmapFactory.decodeStream(). Note: not
* all Formats support all bitmap configs directly, so it is possible that
* the returned bitmap from BitmapFactory could be in a different bitdepth,
* and/or may have lost per-pixel alpha (e.g. JPEG only supports opaque
* pixels).
*
* @param format The format of the compressed image
* @param quality Hint to the compressor, 0-100. 0 meaning compress for
* small size, 100 meaning compress for max quality. Some
* formats, like PNG which is lossless, will ignore the
* quality setting
* @param stream The outputstream to write the compressed data.
* @return true if successfully compressed to the specified stream.
*/
@WorkerThread
public boolean compress(CompressFormat format, int quality, OutputStream stream) ;
- 注意看quality的注释:PNG是lossless的格式,我们会ignore掉quality的设置!!!
- 妈耶,急得我真是国贸腔都给跑出来了。
- 从注释上来看,也就是说只要用的是PNG格式的压缩,quality是不会生效的,因为没法压缩了。
##解决
- 其实解决方案也很简单:换成JPEG格式就好啦
/**
*
*
* @param bmp 待压缩bitmap
* @param expectSizeKb 希望压缩的大小
* @param needRecycle bitmap需要回收吗
* @return : {@link Bitmap}
* @author : MuXi
* @date : 2020/5/8 19:02
*/
private static Bitmap scaleToBitmapMinSize(Bitmap bmp, int expectSizeKb, boolean needRecycle) {
ByteArrayOutputStream output = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.JPEG, 100, output);
int options = 91;
while (output.toByteArray().length > expectSizeKb * 1024) {
output.reset(); // 重置output
bmp.compress(Bitmap.CompressFormat.JPEG, options, output);// 这里压缩options%,把压缩后的数据存放到output
options -= 5;// 每次都减少5大小的质量(其实可以自行调整,每次减少1也行)
if (options <= 0) {
break;
}
}
if (needRecycle) {
bmp.recycle();
}
ByteArrayInputStream isBm = new ByteArrayInputStream(output.toByteArray());// 新的bitmap流,用于后续生成新的bitmap返回
return BitmapFactory.decodeStream(isBm, null, null);
}}
- 上述函数可以方便地对Bitmap进行大小质量压缩。