pull/36/head
parent
b2ac302c45
commit
bced8f3837
@ -0,0 +1,252 @@ |
|||||||
|
package io.legado.app.utils |
||||||
|
|
||||||
|
import android.content.Context |
||||||
|
import android.graphics.Bitmap |
||||||
|
import android.graphics.Bitmap.Config |
||||||
|
import android.graphics.BitmapFactory |
||||||
|
import android.graphics.Canvas |
||||||
|
import android.renderscript.Allocation |
||||||
|
import android.renderscript.Element |
||||||
|
import android.renderscript.RenderScript |
||||||
|
import android.renderscript.ScriptIntrinsicBlur |
||||||
|
import android.view.View |
||||||
|
import io.legado.app.App |
||||||
|
import java.io.IOException |
||||||
|
import kotlin.math.ceil |
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unused", "WeakerAccess") |
||||||
|
object BitmapUtil { |
||||||
|
/** |
||||||
|
* 从path中获取图片信息,在通过BitmapFactory.decodeFile(String path)方法将突破转成Bitmap时, |
||||||
|
* 遇到大一些的图片,我们经常会遇到OOM(Out Of Memory)的问题。所以用到了我们上面提到的BitmapFactory.Options这个类。 |
||||||
|
* |
||||||
|
* @param path 文件路径 |
||||||
|
* @param width 想要显示的图片的宽度 |
||||||
|
* @param height 想要显示的图片的高度 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
fun decodeBitmap(path: String, width: Int, height: Int): Bitmap { |
||||||
|
val op = BitmapFactory.Options() |
||||||
|
// inJustDecodeBounds如果设置为true,仅仅返回图片实际的宽和高,宽和高是赋值给opts.outWidth,opts.outHeight; |
||||||
|
op.inJustDecodeBounds = true |
||||||
|
BitmapFactory.decodeFile(path, op) //获取尺寸信息 |
||||||
|
//获取比例大小 |
||||||
|
val wRatio = ceil((op.outWidth / width).toDouble()).toInt() |
||||||
|
val hRatio = ceil((op.outHeight / height).toDouble()).toInt() |
||||||
|
//如果超出指定大小,则缩小相应的比例 |
||||||
|
if (wRatio > 1 && hRatio > 1) { |
||||||
|
if (wRatio > hRatio) { |
||||||
|
op.inSampleSize = wRatio |
||||||
|
} else { |
||||||
|
op.inSampleSize = hRatio |
||||||
|
} |
||||||
|
} |
||||||
|
op.inJustDecodeBounds = false |
||||||
|
return BitmapFactory.decodeFile(path, op) |
||||||
|
} |
||||||
|
|
||||||
|
/** 从path中获取Bitmap图片 |
||||||
|
* @param path 图片路径 |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
|
||||||
|
fun decodeBitmap(path: String): Bitmap { |
||||||
|
val opts = BitmapFactory.Options() |
||||||
|
|
||||||
|
opts.inJustDecodeBounds = true |
||||||
|
BitmapFactory.decodeFile(path, opts) |
||||||
|
|
||||||
|
opts.inSampleSize = computeSampleSize(opts, -1, 128 * 128) |
||||||
|
|
||||||
|
opts.inJustDecodeBounds = false |
||||||
|
|
||||||
|
return BitmapFactory.decodeFile(path, opts) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 以最省内存的方式读取本地资源的图片 |
||||||
|
* @param context 设备上下文 |
||||||
|
* @param resId 资源ID |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
fun decodeBitmap(context: Context, resId: Int): Bitmap? { |
||||||
|
val opt = BitmapFactory.Options() |
||||||
|
opt.inPreferredConfig = Config.RGB_565 |
||||||
|
//获取资源图片 |
||||||
|
val `is` = context.resources.openRawResource(resId) |
||||||
|
return BitmapFactory.decodeStream(`is`, null, opt) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param context 设备上下文 |
||||||
|
* @param resId 资源ID |
||||||
|
* @param width |
||||||
|
* @param height |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
fun decodeBitmap(context: Context, resId: Int, width: Int, height: Int): Bitmap? { |
||||||
|
|
||||||
|
var inputStream = context.resources.openRawResource(resId) |
||||||
|
|
||||||
|
val op = BitmapFactory.Options() |
||||||
|
// inJustDecodeBounds如果设置为true,仅仅返回图片实际的宽和高,宽和高是赋值给opts.outWidth,opts.outHeight; |
||||||
|
op.inJustDecodeBounds = true |
||||||
|
BitmapFactory.decodeStream(inputStream, null, op) //获取尺寸信息 |
||||||
|
//获取比例大小 |
||||||
|
val wRatio = ceil((op.outWidth / width).toDouble()).toInt() |
||||||
|
val hRatio = ceil((op.outHeight / height).toDouble()).toInt() |
||||||
|
//如果超出指定大小,则缩小相应的比例 |
||||||
|
if (wRatio > 1 && hRatio > 1) { |
||||||
|
if (wRatio > hRatio) { |
||||||
|
op.inSampleSize = wRatio |
||||||
|
} else { |
||||||
|
op.inSampleSize = hRatio |
||||||
|
} |
||||||
|
} |
||||||
|
inputStream = context.resources.openRawResource(resId) |
||||||
|
op.inJustDecodeBounds = false |
||||||
|
return BitmapFactory.decodeStream(inputStream, null, op) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param context 设备上下文 |
||||||
|
* @param fileNameInAssets Assets里面文件的名称 |
||||||
|
* @param width 图片的宽度 |
||||||
|
* @param height 图片的高度 |
||||||
|
* @return Bitmap |
||||||
|
* @throws IOException |
||||||
|
*/ |
||||||
|
@Throws(IOException::class) |
||||||
|
fun decodeBitmap(context: Context, fileNameInAssets: String, width: Int, height: Int): Bitmap? { |
||||||
|
|
||||||
|
var inputStream = context.assets.open(fileNameInAssets) |
||||||
|
val op = BitmapFactory.Options() |
||||||
|
// inJustDecodeBounds如果设置为true,仅仅返回图片实际的宽和高,宽和高是赋值给opts.outWidth,opts.outHeight; |
||||||
|
op.inJustDecodeBounds = true |
||||||
|
BitmapFactory.decodeStream(inputStream, null, op) //获取尺寸信息 |
||||||
|
//获取比例大小 |
||||||
|
val wRatio = ceil((op.outWidth / width).toDouble()).toInt() |
||||||
|
val hRatio = ceil((op.outHeight / height).toDouble()).toInt() |
||||||
|
//如果超出指定大小,则缩小相应的比例 |
||||||
|
if (wRatio > 1 && hRatio > 1) { |
||||||
|
if (wRatio > hRatio) { |
||||||
|
op.inSampleSize = wRatio |
||||||
|
} else { |
||||||
|
op.inSampleSize = hRatio |
||||||
|
} |
||||||
|
} |
||||||
|
inputStream = context.assets.open(fileNameInAssets) |
||||||
|
op.inJustDecodeBounds = false |
||||||
|
return BitmapFactory.decodeStream(inputStream, null, op) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
//图片不被压缩 |
||||||
|
fun convertViewToBitmap(view: View, bitmapWidth: Int, bitmapHeight: Int): Bitmap { |
||||||
|
val bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888) |
||||||
|
view.draw(Canvas(bitmap)) |
||||||
|
return bitmap |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @param options |
||||||
|
* @param minSideLength |
||||||
|
* @param maxNumOfPixels |
||||||
|
* @return |
||||||
|
* 设置恰当的inSampleSize是解决该问题的关键之一。BitmapFactory.Options提供了另一个成员inJustDecodeBounds。 |
||||||
|
* 设置inJustDecodeBounds为true后,decodeFile并不分配空间,但可计算出原始图片的长度和宽度,即opts.width和opts.height。 |
||||||
|
* 有了这两个参数,再通过一定的算法,即可得到一个恰当的inSampleSize。 |
||||||
|
* 查看Android源码,Android提供了下面这种动态计算的方法。 |
||||||
|
*/ |
||||||
|
fun computeSampleSize( |
||||||
|
options: BitmapFactory.Options, |
||||||
|
minSideLength: Int, |
||||||
|
maxNumOfPixels: Int |
||||||
|
): Int { |
||||||
|
|
||||||
|
val initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels) |
||||||
|
|
||||||
|
var roundedSize: Int |
||||||
|
|
||||||
|
if (initialSize <= 8) { |
||||||
|
roundedSize = 1 |
||||||
|
while (roundedSize < initialSize) { |
||||||
|
roundedSize = roundedSize shl 1 |
||||||
|
} |
||||||
|
} else { |
||||||
|
roundedSize = (initialSize + 7) / 8 * 8 |
||||||
|
} |
||||||
|
|
||||||
|
return roundedSize |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private fun computeInitialSampleSize( |
||||||
|
options: BitmapFactory.Options, |
||||||
|
minSideLength: Int, |
||||||
|
maxNumOfPixels: Int |
||||||
|
): Int { |
||||||
|
|
||||||
|
val w = options.outWidth.toDouble() |
||||||
|
val h = options.outHeight.toDouble() |
||||||
|
|
||||||
|
val lowerBound = if (maxNumOfPixels == -1) |
||||||
|
1 |
||||||
|
else |
||||||
|
Math.ceil(Math.sqrt(w * h / maxNumOfPixels)).toInt() |
||||||
|
|
||||||
|
val upperBound = if (minSideLength == -1) 128 else Math.min( |
||||||
|
Math.floor(w / minSideLength), |
||||||
|
Math.floor(h / minSideLength) |
||||||
|
).toInt() |
||||||
|
|
||||||
|
if (upperBound < lowerBound) { |
||||||
|
// return the larger one when there is no overlapping zone. |
||||||
|
return lowerBound |
||||||
|
} |
||||||
|
|
||||||
|
return if (maxNumOfPixels == -1 && minSideLength == -1) { |
||||||
|
1 |
||||||
|
} else if (minSideLength == -1) { |
||||||
|
lowerBound |
||||||
|
} else { |
||||||
|
upperBound |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 高斯模糊 |
||||||
|
*/ |
||||||
|
fun stackBlur(srcBitmap: Bitmap?): Bitmap? { |
||||||
|
if (srcBitmap == null) return null |
||||||
|
val rs = RenderScript.create(App.INSTANCE) |
||||||
|
val blurredBitmap = srcBitmap.copy(Config.ARGB_8888, true) |
||||||
|
|
||||||
|
//分配用于渲染脚本的内存 |
||||||
|
val input = Allocation.createFromBitmap( |
||||||
|
rs, |
||||||
|
blurredBitmap, |
||||||
|
Allocation.MipmapControl.MIPMAP_FULL, |
||||||
|
Allocation.USAGE_SHARED |
||||||
|
) |
||||||
|
val output = Allocation.createTyped(rs, input.type) |
||||||
|
|
||||||
|
//加载我们想要使用的特定脚本的实例。 |
||||||
|
val script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)) |
||||||
|
script.setInput(input) |
||||||
|
|
||||||
|
//设置模糊半径 |
||||||
|
script.setRadius(8f) |
||||||
|
|
||||||
|
//启动 ScriptIntrinsicBlur |
||||||
|
script.forEach(output) |
||||||
|
|
||||||
|
//将输出复制到模糊的位图 |
||||||
|
output.copyTo(blurredBitmap) |
||||||
|
|
||||||
|
return blurredBitmap |
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue