Jetpack Compose Immutable ImageBitmap 传递给 Canvas

问题描述

将不可变图像加载到画布时崩溃

java.lang.IllegalStateException: Immutable bitmap passed to Canvas constructor

在经典的 Android Canvas 和 Compose Canvas 中。

使用下面的代码段是导致 Jetpack Compose 崩溃的原因。

val deferredResource: DeferredResource<ImageBitmap> =
    loadImageResource(id = R.drawable.landscape2)

deferredResource.resource.resource?.let { imageBitmap ->
    val paint = Paint().apply {
        style = PaintingStyle.stroke
        strokeWidth = 1f
        color = Color(0xffFFEE58)

    }
    Canvas(image = imageBitmap).drawRect(0f,0f,100f,paint)
}

这可以用 Bitmap 解决,可以看到 here with

Bitmap workingBitmap = Bitmap.createBitmap(chosenFrame);
Bitmap mutableBitmap = workingBitmap.copy(Bitmap.Config.ARGB_8888,true);
Canvas canvas = new Canvas(mutableBitmap);

我可以使用

ImageBitmap 转换为 Android Bitmap
        val bitmap = imageBitmap.asAndroidBitmap().copy(Bitmap.Config.ARGB_8888,true)

发现也可以使用

Bitmap转换回ImageBitmap
 val newImageBitmap = bitmap.asImageBitmap()

结果我在下面的代码片段上绘制了那个位图

  val canvas = Canvas(newImageBitmap)

    canvas.drawRect(0f,200f,paint = paint)
    canvas.drawCircle(
        Offset(
            newImageBitmap.width / 2 - 75f,newImageBitmap.height / 2 + 75f
        ),150.0f,paint
    )

    Image(bitmap = newImageBitmap)

有没有一种不那么复杂的方式来使用 Canvas 在 ImageBitmap 上绘制而无需在 Bitmap 和 ImageBitmap 之间来回转换?

解决方法

loadImageResource() 使用带有 AndroidImageBitmapBitmap.decodeResource(resources,drawableId) 实现,它请求在没有选项的情况下调用它。

这可能是撰写的限制。您可能需要编写自己的 loadingImageResource(),它会使用可变的 ImageBitmap 调用您自己的 Bitmap 实现。

fun imageFromResource(res: Resources,resId: Int): ImageBitmap {
    return MutableAndroidImageBitmap(BitmapFactory.decodeResource(res,resId,BitmapFactory.Options().apply { inMutable = true }))
}
class MutableAndroidImageBitmap(internal val bitmap: Bitmap) : ImageBitmap 

请注意,绘制此操作将失败,因为在将 asAndroidBitmap() 绘制到基础 ImageBitmap 时,转换 ImageBitmap 会检查 Canvas 的实现。

我想你应该坚持你在问题中陈述的步骤。 asImageBitmap() 不会将 ImageBitmap 转换为 Bitmap 它只是返回包装的内部属性。将 Bitmap 转换为 ImageBitmap 会读取像素数据并创建它的副本。

suspend fun ImageBitmap.mutate(context: CoroutineContext = EmptyCoroutineContext,config: Bitmap.Config) = withContext(context) {
    val workingBitmap = asAndroidBitmap() //this is just access to `bitmap` property
    val mutableBitmap = workingBitmap.copy(config,true)
    workingBitmap.recycle()
    mutableBitmap.asImageBitmap()
}

已打开问题跟踪器 https://issuetracker.google.com/issues/177129056

上的错误