177 lines
7.1 KiB
Kotlin
177 lines
7.1 KiB
Kotlin
package com.gh.common.util
|
||
|
||
import android.graphics.Bitmap
|
||
import android.graphics.BitmapFactory
|
||
import android.graphics.Matrix
|
||
import com.gh.common.constant.Config
|
||
import com.gh.gamecenter.entity.SettingsEntity
|
||
import com.github.piasy.biv.metadata.ImageInfoExtractor
|
||
import com.halo.assistant.HaloApp
|
||
import java.io.File
|
||
import java.io.FileOutputStream
|
||
|
||
/**
|
||
* Created by khy on 02/08/18.
|
||
* 图片压缩工具类
|
||
* 资料参考:https://github.com/zetbaitsu/Compressor
|
||
* 压缩算法:http://gitlab.ghzhushou.com/pm/halo-app-issues/issues/298
|
||
*/
|
||
object CompressImageUtils {
|
||
|
||
private const val compressLimitSize: Long = 50 * 1024
|
||
private const val defaultQuality = 90
|
||
private const val defaultRatio = 2
|
||
private const val defaultCompressBorder = 1280
|
||
|
||
/**
|
||
* 压缩图片并保存到目标文件
|
||
* 该压缩方法是同步执行 请勿在主线程执行
|
||
* 返回源文件的三种情况:小于特定值,图片类型为GIF,压缩失败
|
||
* ---------------------------------------------------------------------------------------------
|
||
* 关于压缩格式问题:
|
||
* 1.图片详情对动态Webp/静态Webp的判断存在问题,导致部分静态Webp图片误判为动态Webp而直接委托Fresco处理,出现无法缩放问题
|
||
* 2.为了解决上述问题,如果压缩是检测到是动态Webp时直接压缩为JPEG,这样就能解决图片详情无法缩放问题(todo 这样会导致动态Webp无法播放)
|
||
*/
|
||
@Throws(Exception::class)
|
||
fun compressImageAndSaveToFile(imageFile: File, compressGif: Boolean): File {
|
||
val imageType = ImageInfoExtractor.getImageType(imageFile)
|
||
// 小于某一个设定的值时,直接返回原图
|
||
if (imageType != ImageInfoExtractor.TYPE_ANIMATED_WEBP && imageFile.length() < getImageSetting().processLimitSize) {
|
||
return imageFile
|
||
}
|
||
|
||
val cacheDir = getImageCachePatch()
|
||
val parentFile = cacheDir.parentFile
|
||
if (!parentFile.exists()) parentFile.mkdirs()
|
||
var fileOutputStream: FileOutputStream? = null
|
||
|
||
try {
|
||
// 确定图片类型
|
||
val options = BitmapFactory.Options()
|
||
options.inJustDecodeBounds = true
|
||
BitmapFactory.decodeFile(imageFile.absolutePath, options)
|
||
val formatType = if (options.outMimeType.contains("png")) {
|
||
Bitmap.CompressFormat.PNG
|
||
} else if (options.outMimeType.contains("gif") && !compressGif) { // gif直接返回原图
|
||
return imageFile
|
||
} else {
|
||
Bitmap.CompressFormat.JPEG
|
||
}
|
||
|
||
fileOutputStream = FileOutputStream(cacheDir)
|
||
// write the compressed bitmap at the destination specified by destinationPath.
|
||
decodeSampledBitmapFromFile(imageFile).compress(formatType, getImageSetting().quality, fileOutputStream)
|
||
|
||
if (cacheDir.length() == 0L) {
|
||
return imageFile //预防压缩失败
|
||
}
|
||
return cacheDir
|
||
} catch (e: Exception) {
|
||
e.printStackTrace()
|
||
if (cacheDir.exists()) {
|
||
cacheDir.delete()
|
||
}
|
||
} finally {
|
||
if (fileOutputStream != null) {
|
||
fileOutputStream.flush()
|
||
fileOutputStream.close()
|
||
}
|
||
}
|
||
return imageFile
|
||
}
|
||
|
||
private fun getImageCachePatch(): File {
|
||
// return File(Environment.getExternalStorageDirectory().absolutePath + "/Pictures/test/" + System.currentTimeMillis() + ".jpg")
|
||
// 统一用jpg保存应该没有影响吧
|
||
return File(HaloApp.getInstance().application.cacheDir.absolutePath + File.separator + System.currentTimeMillis() + ".jpg")
|
||
}
|
||
|
||
// 根据图片获取压缩后的位图
|
||
@Throws(Exception::class)
|
||
private fun decodeSampledBitmapFromFile(imageFile: File): Bitmap {
|
||
// First decode with inJustDecodeBounds=true to check dimensions
|
||
val options = BitmapFactory.Options()
|
||
options.inJustDecodeBounds = true
|
||
BitmapFactory.decodeFile(imageFile.absolutePath, options)
|
||
|
||
// Raw height and width of image
|
||
val height = options.outHeight
|
||
val width = options.outWidth
|
||
var inSampleSize = 1
|
||
|
||
var compressType: CompressType? = null
|
||
|
||
val longSide = Math.max(height, width) //最长边
|
||
val shortSide = Math.min(height, width) //最短边
|
||
val scale = longSide.toFloat() / shortSide // 长短边比例
|
||
val compressLimit = getImageSetting().size
|
||
|
||
if (longSide > compressLimit && shortSide > compressLimit) {
|
||
if (scale > getImageSetting().ratio) {
|
||
inSampleSize = if (shortSide / compressLimit == 0) 1 else shortSide / compressLimit
|
||
compressType = CompressType.LIMIT_SHORT // 横向长方形
|
||
} else {
|
||
inSampleSize = if (longSide / compressLimit == 0) 1 else longSide / compressLimit
|
||
compressType = CompressType.LIMIT_LONG // 纵向长方形
|
||
}
|
||
} else if (longSide > compressLimit && shortSide < compressLimit) {
|
||
if (scale <= getImageSetting().ratio) {
|
||
inSampleSize = if (longSide / compressLimit == 0) 1 else longSide / compressLimit
|
||
compressType = CompressType.LIMIT_LONG
|
||
}
|
||
}
|
||
|
||
// Calculate inSampleSize
|
||
options.inSampleSize = inSampleSize
|
||
|
||
// Decode bitmap with inSampleSize set
|
||
options.inJustDecodeBounds = false
|
||
|
||
var scaledBitmap = BitmapFactory.decodeFile(imageFile.absolutePath, options)
|
||
|
||
val matrix = Matrix() // 精确缩放
|
||
if (compressType != null) {
|
||
val targetMatrixScale = if (compressType == CompressType.LIMIT_SHORT) {
|
||
if (scaledBitmap.width > scaledBitmap.height) {
|
||
compressLimit.toFloat() / scaledBitmap.height
|
||
} else {
|
||
compressLimit.toFloat() / scaledBitmap.width
|
||
}
|
||
} else {
|
||
if (scaledBitmap.width > scaledBitmap.height) {
|
||
compressLimit.toFloat() / scaledBitmap.width
|
||
} else {
|
||
compressLimit.toFloat() / scaledBitmap.height
|
||
}
|
||
}
|
||
matrix.setScale(targetMatrixScale, targetMatrixScale)
|
||
try {
|
||
scaledBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.width, scaledBitmap.height, matrix, true)
|
||
} catch (ignore: OutOfMemoryError) {
|
||
|
||
}
|
||
}
|
||
return scaledBitmap
|
||
}
|
||
|
||
private fun getImageSetting(): SettingsEntity.Image {
|
||
val settings = Config.getSettings()
|
||
if (settings?.image != null) {
|
||
return settings.image!!
|
||
}
|
||
val image = SettingsEntity.Image()
|
||
image.processLimitSize = compressLimitSize
|
||
image.size = defaultCompressBorder
|
||
image.ratio = defaultRatio
|
||
image.quality = defaultQuality
|
||
return image
|
||
}
|
||
}
|
||
|
||
enum class CompressType {
|
||
// 0: 短边等比压缩至1280 H:长边 W:短边
|
||
LIMIT_SHORT,
|
||
|
||
// 1: 取长边等比压缩至1280 H:短边 W: 长边
|
||
LIMIT_LONG
|
||
} |