参考框架:
个人实现(kotlin语言)
前言
图片可以划分为:内存中的图片大小(bitmap)和 图片文件大小。bitmap大小是从文件中读取时,加载到内存中,若过大,则容易导致OOM问题。而图片文件大小,在上传到服务器时,一般也是需要限定图片大小的。
公式基础
1,Bitmap大小 = 【宽 * 高 * 单个像素点大小(色深)】
1)ARGB_8888单个像素点大小 为 4byte; RGB_565单个像素点大小 为 2byte
2,图片文件大小 约等于 【宽 * 高 * (位深/8) * (1 - 压缩算法效率)】
1)bmp位图,储存时,无压缩
2)jpg、webp、png格式文件大小,小于bmp位图的文件大小,则是由图像压缩算法造成
3) 位深大小,一般为:8位、16位、24位
需要探索两个问题,1,bitmap压缩和图片文件压缩 2,色深和位深的异同
Android Bitmap压缩
1)改变 宽、高;加载时,指定option.inSampleSize,加载时,对文件进行采样加载
val option = BitmapFactory.Options()
option.inSampleSize = scale
return BitmapFactory.decodeFile(sourcePath, option)
2)改变 单个像素点大小,指定option.inPreferredConfig,加载时,确定将要加载的单个像素点大小
val option = BitmapFactory.Options()
option.inPreferredConfig = Bitmap.Config.RGB_565
return BitmapFactory.decodeFile(sourcePath, option)
Android 图片文件压缩
1)改变 宽、高;加载或生成bitmap时,指定新的宽、高
2)改变 位深;【很遗憾,经测试,这无法通过代码改变】
3)改变 压缩算法效率
* 改变 图片格式,不同图片格式对应的压缩效率不同,但一般而言,效率越高、耗时越长
* 改变 储存时清晰度,即Android中常用的,保存图片的quality(这是通过降低图片的局部清晰度,实现的有损压缩)
压缩文件实现
1)采用的压缩策略有 ①改变宽高(计算采样率) ②改变图片格式 ③改变储存时清晰度
val scale = computeScale(sourcePath) // 计算采样率
val sourceBitmap = loadBitmap(sourcePath, scale) // 加载出 bitmap,第一次压缩
saveBitmap(sourceBitmap, Bitmap.CompressFormat.JPEG, quality, resultFile) // 保存bitmap,第二次压缩
2)动态计算采样率,参考Luban
/**
* 依据图片宽高,确定图片采样率
* @param sourcePath 输入的文件
*/
fun computeScale(sourcePath: String): Int {
val option = BitmapFactory.Options()
option.inJustDecodeBounds = true
option.inSampleSize = 1
BitmapFactory.decodeFile(sourcePath, option)
// 原始宽高、进行奇偶处理
val sourceWidth = if (option.outWidth % 2 == 1) option.outWidth + 1 else option.outWidth
val sourceHeight = if (option.outHeight % 2 == 1) option.outHeight + 1 else option.outHeight
val longSide = Math.max(sourceWidth, sourceHeight)
val shortSide = Math.min(sourceWidth, sourceHeight)
val scale = shortSide.toFloat() / longSide
if (scale <= 1 && scale > 0.5625) {
if (longSide < 1664) {
return 1
} else if (longSide < 4990) {
return 2
} else if (longSide > 4990 && longSide < 10240) {
return 4
} else {
return if (longSide / 1280 == 0) 1 else longSide / 1280
}
} else if (scale <= 0.5625 && scale > 0.5) {
return if (longSide / 1280 == 0) 1 else longSide / 1280
} else {
return Math.ceil(longSide / (1280.0 / scale)).toInt()
}
}
3) 加载出图片,因为是采样率可能不为1,因此,加载出来的bitmap宽高,其实和文件宽高已经不一样了;也就起到了修改图片宽高的作用
fun loadBitmap(sourcePath: String, scale: Int): Bitmap {
val option = BitmapFactory.Options()
option.inJustDecodeBounds = false
option.inSampleSize = scale
return BitmapFactory.decodeFile(sourcePath, option)
}
4)bitmap本身是没有格式的,确定输出的格式,即改变了
/**
* 保存图片,参数未做校验
* @param bitmap 准备保存的图片
* @param format 图片的格式
* @param quality 质量压缩的参数【quality(1-100),100表示不压缩】
* @param resultFile 输出到的文件夹
*/
fun saveBitmap(bitmap: Bitmap, format: Bitmap.CompressFormat, quality: Int, resultFile: File): Boolean {
// 保存图片,并进行压缩
var isSuccess = false
var fileOutputStream: FileOutputStream? = null
try {
fileOutputStream = FileOutputStream(resultFile)
isSuccess = bitmap.compress(format, quality, fileOutputStream)
} catch (ex: IOException) {
ex.printStackTrace()
} finally {
if (null != fileOutputStream) {
fileOutputStream.flush()
fileOutputStream.close()
}
}
return isSuccess
}
色深和位深,有什么异同
百度百科的结果:
位深:计算机储存每个像素所需要的大小
色深:计算机显示出影像时,每个像素所需要的大小
因此,可以看到,他们描述的内容相同,都是单个像素。但是状态不一样,位深是储存,而色深是显示
测试方案:
Step 1,从本地加载图片文件到内存,为loadBitmap
Step 2,分别按照ARGB_8888和RGB_565格式,生成bitmap8888和bitmap565,并记录内存大小
Step 3,分别将bitmap8888和bitmap565,储存为bmp格式,分别为文件:file8888和file565
说明:
1) 储存为bmp格式,以防不同格式,对压缩算法产生影响(实际上的确会产生影响)
2)若file8888和bitmap8888大小相同,而file565和bitmap565大小相同,则说明,位深等同于色深
不然,位深不等同于色深。
测试结果
文件 | 原始文件 (宽 * 高 大小) | file8888 (宽 * 高 大小) | bitmap8888 (大小) | file565 (宽 * 高 大小) | bitmap565 (大小) |
A | 3120*4160 3597k | 1560*2080 9506k | 12675k | 1560*2080 9506k | 6337k |
B | 4912*3264 2414k | 2456*1632 11742 | 15657k | 2456*1632 11742 | 7828k |
C | 314*9675 1747k | 413*9675 11715k | 15608k | 413*9675 11715k | 7804k |
已知:ARGB_8888 = 4, RGB = 2
1,1560 * 2080 * 4 / 1024 = 12675,符合公式:
Bitmap大小 = 【宽 * 高 * 单个像素点大小(色深)】
2,file8888 != bitmap8888, file565 != bitmap565,说明位深不等同于色深
3,file8888/3 = bitmap8888/4, file565/3 = bitmap565/3
可以分析出,位深都为24位,与生成bitmap的色深格式无关。而我们实际加载的图片是相同的,因此可以理解成"位深是图片产生时携带的参数"
测试结论:
位深:是计算机储存像素的单位大小,由PhotoShop等软件生成图片时,就确定了通道个数,确定了大小
色深:是计算机显示像素的单位大小,可以在加载图片时,选择加载格式确定大小
位深 != 色深
本文总结:
1,公式
1)内存:Bitmap大小 = 【宽 * 高 * 单个像素点大小(色深)】
2)文件:图片大小 约等于 【宽 * 高 * (位深/8) * (1 - 压缩算法效率)】
2,位深和色深不同,位深在产生原始图时就已经确定了。可以判断出,图片文件的大小只取决于宽、高和压缩算法效率
3,实现了将图片进行压缩,链接为:CompressManager.kt