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