package com.grkj.iscs.util import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory import android.graphics.Canvas import android.graphics.ImageFormat import android.graphics.Rect import android.graphics.YuvImage import android.os.Environment import androidx.annotation.DrawableRes import androidx.core.content.ContextCompat import com.bumptech.glide.Glide import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.engine.GlideException import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.target.Target import com.grkj.iscs.util.log.LogUtil import java.io.ByteArrayOutputStream import java.io.File import java.io.FileOutputStream import java.io.IOException object BitmapUtil { fun bitmapToFile(bitmap: Bitmap, fileName: String): File? { // 创建一个临时文件 val storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) if (!storageDir.exists()) { storageDir.mkdirs() } val file = File(storageDir, fileName) return try { // 创建文件输出流 val fos = FileOutputStream(file) // 将 Bitmap 压缩并写入文件 bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos) // 刷新并关闭输出流 fos.flush() fos.close() file } catch (e: IOException) { e.printStackTrace() null } } fun bitmapToByteArray(bitmap: Bitmap, format: Bitmap.CompressFormat = Bitmap.CompressFormat.PNG, quality: Int = 100): ByteArray { val stream = ByteArrayOutputStream() // 将 Bitmap 压缩并写入到 ByteArrayOutputStream 中 bitmap.compress(format, quality, stream) // 获取字节数组 return stream.toByteArray() } fun loadBitmapFromUrl( ctx: Context, url: String?, placeholder: Int? = null, reqWidth: Int? = null, reqHeight: Int? = null, callback: (Bitmap?) -> Unit ) { try { val builder = Glide.with(ctx) .asBitmap() .load(url) if (reqWidth != null && reqHeight != null) { builder.override(reqWidth, reqHeight) } if (placeholder != null) { builder.placeholder(placeholder) } builder.listener(object : RequestListener { override fun onLoadFailed( e: GlideException?, model: Any?, target: Target?, isFirstResource: Boolean ): Boolean { Executor.runOnMain { callback(null) } return false } override fun onResourceReady( resource: Bitmap?, model: Any?, target: Target?, dataSource: DataSource?, isFirstResource: Boolean ): Boolean { Executor.runOnMain { callback(resource) } return false } }).submit() } catch (e: Exception) { e.printStackTrace() LogUtil.e("loadBitmapFromUrl error: ${e.message} - $url") Executor.runOnMain { callback(null) } } } fun getResizedBitmapFromMipmap( context: Context, resId: Int, reqWidth: Int, reqHeight: Int ): Bitmap { // First decode with inJustDecodeBounds=true to check dimensions val options = BitmapFactory.Options() options.inJustDecodeBounds = true BitmapFactory.decodeResource(context.resources, resId, options) // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight) // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false val scaledBitmap = BitmapFactory.decodeResource(context.resources, resId, options) // Resize the bitmap if necessary return Bitmap.createScaledBitmap(scaledBitmap, reqWidth, reqHeight, false) } private fun calculateInSampleSize( options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int ): Int { // Raw height and width of image val height = options.outHeight val width = options.outWidth var inSampleSize = 1 if (height > reqHeight || width > reqWidth) { val halfHeight = height / 2 val halfWidth = width / 2 // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2 } } return inSampleSize } fun getResizedBitmapFromDrawable( context: Context, resId: Int, reqWidth: Int, reqHeight: Int ): Bitmap? { val drawable = context.resources.getDrawable(resId) if (drawable == null) { LogUtil.e("Drawable is null for resource ID: $resId") return null } // Create a bitmap with the specified width and height val bitmap = Bitmap.createBitmap(reqWidth, reqHeight, Bitmap.Config.ARGB_8888) val canvas = Canvas(bitmap) // Scale the drawable to fit the bitmap dimensions drawable.setBounds(0, 0, reqWidth, reqHeight) drawable.draw(canvas) return bitmap } /** * 将NV21格式的数据转换为Bitmap。(效率低,用NV21ToBitmap类) * * @param nv21 NV21格式的字节数组 * @param width 图像的宽度 * @param height 图像的高度 * @return 转换后的Bitmap对象 */ fun convertNV21ToBitmap(nv21: ByteArray?, width: Int, height: Int): Bitmap? { val yuvImage = YuvImage(nv21, ImageFormat.NV21, width, height, null) val outputStream = ByteArrayOutputStream() yuvImage.compressToJpeg(Rect(0, 0, width, height), 100, outputStream) val imageBytes = outputStream.toByteArray() return try { val bitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size) outputStream.close() bitmap } catch (e: Exception) { e.printStackTrace() null } } }