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