package com.img.rabbit.viewmodel import android.content.Context import android.graphics.Bitmap import android.net.Uri import android.os.Build import android.util.Log import androidx.compose.runtime.mutableStateOf import com.img.rabbit.bean.local.ErrorBean import com.img.rabbit.provider.api.ApiManager import com.img.rabbit.utils.DownLoadUtils import com.img.rabbit.utils.FileUtils import com.img.rabbit.utils.ImageUtils import com.img.rabbit.utils.PhotoCutter import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody import okhttp3.RequestBody import java.io.File import java.io.IOException import kotlin.text.ifEmpty class CutoutViewModel : BaseViewModel() { private val targetPath = FileUtils.instance?.cacheImageDir?.absolutePath?:"" private val targetFileName = "cutoutCache.png" // 错误状态 val errorState = mutableStateOf(null) // 本地抠图 fun cutoutImageFromLocal(context: Context, uri: Uri, onResult: (isSuccess: Boolean, croppedBitmap: Bitmap?) -> Unit) { // 执行抠图操作 Thread { try { val originalBitmap = ImageUtils.getBitmapFromUri(context, uri) originalBitmap?.let { bitmap -> PhotoCutter.cutPureHead(bitmap) { croppedBitmap -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { context.mainExecutor.execute { onResult(true, croppedBitmap) } } } } } catch (e: Exception) { Log.e("CutoutScreen", "抠图失败: ${e.message}") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { context.mainExecutor.execute { onResult(false, null) } } } }.start() } //服务器抠图 fun cutoutImageFromService(context: Context, uri: Uri, onResult: (isSuccess: Boolean, croppedBitmap: Bitmap?) -> Unit) { mLaunch { try { // 1. 获取并压缩图片为 PNG (后端示例中使用了 .png,可能只支持 PNG) val bitmap = ImageUtils.getBitmapFromUri(context, uri) ?: throw IOException("无法获取图片") val outputStream = java.io.ByteArrayOutputStream() // PNG 是无损的,质量参数 100 即可 bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream) val compressedBytes = outputStream.toByteArray() bitmap.recycle() // 2. MediaType 修改为 image/png val requestFile = RequestBody.create("image/png".toMediaTypeOrNull(), compressedBytes) // 3. 构造 MultipartBody.Part,关键:强制给文件名加上 .png 后缀 // 不要直接用 uri.lastPathSegment,除非你能确定它带后缀 val fileName = "cutout_${System.currentTimeMillis()}.png" val filePart = MultipartBody.Part.createFormData("file", fileName, requestFile) // 4. 发起请求 val response = ApiManager.serviceVo.cutoutImage(filePart) if (response.status) { Log.i("CutoutViewModel", "抠图成功: ${response.data}") val resultUrl = response.data.toString() // 将 http 替换为 https //val resultUrl = response.data.toString().replace("http://", "https://") saveUrlToCache(resultUrl, onResult) } else { errorState.value = ErrorBean(response.code.toString(), response.message.ifEmpty { "抠图失败" }) onResult(false, null) } } catch (e: Exception) { // 捕获更广泛的异常 e.printStackTrace() Log.e("CutoutViewModel", "服务请求异常: ${e.message}") onResult(false, null) } } } private fun saveUrlToCache(url: String, onResult: (isSuccess: Boolean, croppedBitmap: Bitmap?) -> Unit) { try { val targetFile = File(targetPath,targetFileName) if(targetFile.exists()){ targetFile.delete() } DownLoadUtils.getInstance() .initUrl(url, null) .setFilePath(targetPath) .setFileName(targetFileName) .setDownCallBack(object : DownLoadUtils.DownCallBack { override fun success(file: File) { // 3. 下载成功后,将文件解析为 Bitmap 并回调给 UI val bitmap = android.graphics.BitmapFactory.decodeFile(file.absolutePath) onResult(true, bitmap) } override fun fail(str: String) { onResult(false, null) } override fun progress(position: Long) { // 如果需要进度可以在这里处理 } override fun total(total: Long) { // 获取总大小 } override fun cancel() { onResult(false, null) } }) .down() }catch (e: Exception){ e.printStackTrace() onResult(false, null) } } }