Files
assistant-android/app/src/main/java/com/gh/common/util/CompressImageUtils.kt
2020-05-06 17:59:39 +08:00

177 lines
7.1 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}