Android 基于Bitmap的四种图片压缩方式
作者:sakuqi 发布时间:2022-06-13 11:50:21
目录
知识点介绍
正文
1、质量压缩
2、采样率压缩
3、缩放法压缩
4、RGB_565 通过改变图片格式来实现压缩
总结
知识点介绍
Android 中图片主要以 Bitmap 的形式存在,所以压缩图片主要就是减少 Bitmap 的大小。Bitmap 的大小可以通过如下的公式计算得到:size = width * height * 单个像素所占字节数。因此压缩图片通过改变公式中的三个变量即可实现。
单个像素所占空间大小在 Android 中有多种,详见如下
格式 | 所占空间 | 说明 |
---|---|---|
Bitmap.Config.ALPHA_8 | 1B | 该种格式表示图片只有透明度没有颜色,1个像素占用8位 |
Bitmap.Config.ARGB_4444 | 2B | 该种格式表示图片透明通道 A 及颜色 R、G、B 各占用4位,共16位 |
Bitmap.Config.ARGB_8888 | 4B | 该种格式表示图片透明通道 A 及颜色 R、G、B 各占用8位,共32位 |
Bitmap.Config.RGB_565 | 2B | 该种格式表示图片没有透明通道,颜色 R、G、B 各占用5、6、6位,共16位 |
Android 中加载图片默认用的是 ARGB_8888 格式,所以加载一张3000 * 4000 的图片默认占用的空间为 45MB 左右,这个值还是很大的😂
测试代码
fun showBitmapInfo(bitmap: Bitmap){
Log.d("Tag","压缩后的图片大小:${bitmap.byteCount/1024/1024}MB,宽度:${bitmap.width},高度:${bitmap.height}")
}
结果
正文
接下来介绍四种压缩方式
1、质量压缩
质量压缩主要通过 Bitmap.compress()实现,方法介绍
/**
*
* @param format 压缩图像的格式
* @param quality 提示压缩机,0-100。 根据Bitmap.CompressFormat不同,该值的解释也不同。
* @param stream –写入压缩数据的输出流。
* @return 如果成功压缩到指定的流,则为true
*/
public boolean compress(CompressFormat format, int quality, OutputStream stream) {
}
CompressFormat 表示图片压缩格式,Android 源码中包含了五种格式
格式名 | 解释 |
---|---|
CompressFormat.JPEG | 压缩为JPEG格式。 quality 0表示压缩为最小大小。 100表示压缩以获得最大视觉质量。 |
CompressFormat.PNG | 压缩为PNG格式。 PNG是无损的,因此quality被忽略。 |
CompressFormat.WEBP | 压缩为WEBP格式。 quality 0表示压缩为最小大小。 100表示压缩以获得最大视觉质量。 从Build.VERSION_CODES.Q ,值100导致文件采用无损WEBP格式。 否则,文件将为有损WEBP格式 |
CompressFormat.WEBP_LOSSY | 压缩为WEBP有损格式。 quality 0表示压缩为最小大小。 100表示压缩以获得最大视觉质量。 |
CompressFormat.WEBP_LOSSLESS | 压缩为WEBP无损格式。 quality是指投入多少精力进行压缩。 值0表示快速压缩,导致文件大小相对较大。 100表示要花费更多时间进行压缩,从而使文件更小。 |
测试代码
/**
* 压缩图片质量
*/
fun getCompressBitmap(bitmap: Bitmap,quality:Int): Bitmap {
val baos = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos)
val byte = baos.toByteArray()
val ins = ByteArrayInputStream(byte)
val bm = BitmapFactory.decodeStream(ins)
ins.close()
baos.close()
return bm
}
效果
根据上面的日志,你会看到质量压缩并不能改变图片在内存中的大小,因为质量压缩既不能改变图片分辨率也不能改变图片的单个像素大小。
那么你可能有些疑问:既然不能改变大小,那么还费这么大功夫转化而且图片还失真是为了什么?
答:源码中对于compress方法的解释是,将位图的压缩版本写入指定的输出流。所以应该是对输出流中的字节数有影响
验证
val baos = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos)
val byte = baos.toByteArray()
Log.d("Tag","quality=$quality,byte-size=${byte.size}")
结果真的是对输出流的字节数有影响
2、采样率压缩
BitmapFactory.Options 中有个属性 inSampleSize,系统中采样率压缩就是通过该属性
/**
* 如果设置为大于1的值,则请求解码器对原始图像进行二次采样,返回较小的图像以节省内存。
* 样本大小是任一维度中与解码后的位图中的单个像素相对应的像素数。 例如,inSampleSize == 4
* 返回的图像为原始宽度/高度的1/4,像素数目的1/16。 任何小于等于1的值都与1相同。
* 注意:解码器使用基于2的幂的最终值,任何其他值将四舍五入为最接近的2的幂。
**/
public int inSampleSize;
直接上代码
/**
* 根据设定的宽高计算缩放比
*/
fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
val height = options.outHeight
val width = options.outWidth
var inSampleSize = 1
if (height > reqHeight || width > reqWidth) {
val heightRatio = round(height.toFloat() / reqHeight.toFloat()).toInt()
val widthRatio = round(width.toFloat() / reqWidth.toFloat()).toInt()
inSampleSize = if (heightRatio < widthRatio) heightRatio else widthRatio
}
return inSampleSize
}
/**
* 获取缩放后的图片
*/
fun getSmallBitmap(filePath: String,reqWidth: Int,reqHeight: Int): Bitmap {
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true //不加载 bitmap 进内存,只获取他的基本信息
BitmapFactory.decodeFile(filePath, options)
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
options.inJustDecodeBounds = false
return BitmapFactory.decodeFile(filePath, options)
}
结果
采样率压缩的方式使用的还是挺多的,因为我们获取到的图片它的尺寸可能很大,但是我们在手机上显示的可能不需要那么大,那我们就将图片缩放成我们需要的大小。
3、缩放法压缩
这种方法主要是依赖 Matrix 矩阵变换的方式对图片进行处理。Matrix 中有很多对图片变换的 api 这里只使用它的缩放功能,其他功能可以自行了解
代码
/**
* 通过矩阵缩放
*/
fun matrixBitmap(bitmap: Bitmap,scale:Float):Bitmap{
val matrix = Matrix()
matrix.setScale(scale,scale)
var bm = Bitmap.createBitmap(bitmap,0,0,bitmap.width,bitmap.height,matrix,true)
return bm
}
当设置缩放比为0.5时,图片整体就缩放为原来的1/4
4、RGB_565 通过改变图片格式来实现压缩
系统默认使用的是ARGB_8888的格式,所以我们只要改变这个 options 值就能实现
fun rgb565Bitmap(filePath: String):Bitmap{
val options = BitmapFactory.Options()
options.inPreferredConfig = Bitmap.Config.RGB_565
var bitmap = BitmapFactory.decodeFile(filePath,options)
return bitmap
}
结果图片变成了原图的一半
总结
对于图片的压缩,首先可以先将图片格式改为 RGB_565,这样图片先减小一半,然后对于图片的显示可以使用采样率压缩或者缩放压缩的方式将图片的分辨率改为我们显示的大小,如果是要将图片上传服务器那么可以使用质量压缩的方式,但是这种方式不支持 png 格式的图片。
来源:https://juejin.cn/post/6959840823261265957


猜你喜欢
- 首先:看问题图,如下可以激活ide的网址很多,估计是个团队或者个人,直接买了全部产品的一年的有效期。而且还是会一直更新下去的。因为,后来我自
- 实践过程效果代码public partial class Form1 : Form{ public Form1()
- 发现问题最近工作中利用JNA 调用 dll 库时保错,错误如下:///////////////// 通过 JNA 引入 DLL 库 ////
- 模式介绍命令模式(Command Pattern) :在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被
- 一、Hash加密,使用HashAlgorithm哈希算法类的派生类(MD5、SHA1等)特点:只能加密,不可逆。可对目标信息生成一段特定长度
- 前言最近一段时间看了一些介绍ViewDragHelper的博客,感觉这是一个处理手势滑动的神奇,看完以后就想做点东西练练手,于是就做了这个A
- 本文实例为大家分享了Java控制台实现猜拳游戏的具体代码,供大家参考,具体内容如下1、目标通过控制台实现一个人机对战的猜拳游戏,用户通过输入
- 由于在项目中要实现用户注册的邮箱激活以及忘记密码重置密码功能,所以通过查阅资料做了一个简单的设计和实现。邮箱激活背景:几乎每个网站或论坛之类
- 前言在我的申请下,公司终于购买了一台基于Android12.0的手机,然后我就开心的拿去安装测试了,发现程序崩溃了,于是我这里就写下来,An
- 1.下载文件,将文件保存到本地。(只试用excel);2.对文件的标题进行检验;3.获取导入的批次(取一个表的一个值,加1);4.循环获取文
- 在项目中,有时候会用到领域枚举和DTO枚举的映射和转换。有一个现实的问题是:如果领域枚举项发生变化,而DTO枚举项没有及时更新,这样会造成映
- 序本文主要研究一下java9 gc log参数的迁移。统一JVM及GC的Loggingjava9引进了一个统一的日志框架,把gc相关的log
- 实现形式elevationMaterial Design提供了View的阴影效果设置。主要由两个属性决定:elevation和transla
- using System;using System.Runtime.InteropServices;using System.Windows
- 本文以一个C#的SQL数据库字串操作函数为例,说明如何实现对SQL字符串过滤、检测SQL是否有危险字符、修正sql语句中的转义字符,确保SQ
- 本文实例为大家分享了Java实现分页功能的具体代码,供大家参考,具体内容如下不用根据改变SQL的形式去查询; 直接查询所有的数据,根据页码自
- 本文实例为大家分享了android实现点击图片全屏展示的具体代码,供大家参考,具体内容如下MainActivity:public class
- 快速排序过程没有既不浪费空间又可以快一点的排序算法呢?那就是“快速排序”!光听这个名字是不是就觉得很高端呢。假设我们现在对“52 39 67
- 初步的想法是用两个recordset,一个从SQL取数据,一个往Access里面插入数据 因为表的字段比较多,所以只好用一个循环while
- 前言在Web应用开发过程中,一般都涵盖一些常用功能的实现,如数据库访问、异常处理、消息队列、缓存服务、OSS服务,以及接口日志配置,接口文档