You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Compose Canvas中如何计算被“擦除”图像的可见百分比?

在Jetpack Compose中计算被擦除图像的可见百分比

你在Jetpack Compose中实现了双图触摸擦除效果,想要计算被擦除图像的可见百分比,判断是否已擦除至少70%。以下是针对你的需求和思路的优化方案:

现有实现代码

val overlayImageLoaded = rememberAsyncImagePainter(
    model = overlayImage,
)
val baseImageLoaded = rememberAsyncImagePainter(
    model = baseImage
)
Canvas(modifier = modifier
    .size(220.dp)
    .clip(RoundedCornerShape(size = 16.dp))
    .pointerInteropFilter {
        when (it.action) {
            MotionEvent.ACTION_DOWN -> {
                currentPath.moveTo(it.x, it.y)
            }

            MotionEvent.ACTION_MOVE -> {
                onMovedOffset(it.x, it.y)
            }
        }
        true
    }) {

    with(overlayImageLoaded) {
        draw(size = Size(size.width, size.height))
    }

    movedOffset?.let {
        currentPath.addOval(oval = Rect(it, currentPathThickness))
    }

    clipPath(path = currentPath, clipOp = ClipOp.Intersect) {
        with(baseImageLoaded) {
            draw(size = Size(size.width, size.height))
        }
    }
}

你的思路分析

  • 思路1:存储onMovedOffset结合画布尺寸计算——核心逻辑可行,但直接存坐标列表计算繁琐,可优化为基于路径/图形几何计算
  • 思路2:转Bitmap对比像素——直观但性能开销高,适合精确判断但需优化触发时机

推荐解决方案

方法一:基于路径几何计算(高效低开销)

利用你已有的currentPath记录擦除区域,通过几何公式或Path工具计算覆盖面积,快速得到擦除比例,适合近似判断70%的需求。

实现步骤

  1. 记录所有擦除的椭圆区域(或直接维护合并后的路径)
  2. 计算单个椭圆面积并累加(处理重叠可选择合并路径后计算)
  3. 对比画布总面积,得出擦除百分比

代码示例

// 记录所有擦除的椭圆Rect
val erasedOvals = remember { mutableStateListOf<Rect>() }

// 在触摸移动时更新路径和椭圆记录
MotionEvent.ACTION_MOVE -> {
    val ovalRect = Rect(it.x - currentPathThickness/2, it.y - currentPathThickness/2,
        it.x + currentPathThickness/2, it.y + currentPathThickness/2)
    erasedOvals.add(ovalRect)
    currentPath.addOval(ovalRect)
    onMovedOffset(it.x, it.y)
}

// 计算已擦除面积(近似,忽略重叠)
val totalErasedArea = remember(erasedOvals) {
    erasedOvals.sumOf { rect ->
        val radius = rect.width / 2f
        Math.PI * radius * radius
    }
}

// 计算擦除百分比
val canvasArea = size.width * size.height
val erasedPercentage = (totalErasedArea / canvasArea) * 100
val isErasedEnough = erasedPercentage >= 70f

如果需要精确处理重叠区域,可合并路径后用PathMeasure计算面积:

val mergedPath = remember(erasedOvals) {
    Path().apply {
        erasedOvals.forEach { addOval(it) }
    }
}

val pathMeasure = remember(mergedPath) { PathMeasure(mergedPath, false) }
val preciseErasedArea = remember(mergedPath) {
    var area = 0.0
    val length = pathMeasure.length
    val pos = FloatArray(2)
    var prevPos = FloatArray(2)
    pathMeasure.getPosTan(0f, pos, null)
    prevPos = pos.copyOf()
    
    var step = 0.5f
    while (step < length) {
        pathMeasure.getPosTan(step, pos, null)
        area += (prevPos[0] * pos[1] - pos[0] * prevPos[1]) / 2.0
        prevPos = pos.copyOf()
        step += 0.5f
    }
    Math.abs(area)
}

方法二:基于Bitmap像素对比(精确但需优化)

适合需要像素级精确判断的场景,建议在用户停止触摸后再触发计算,避免频繁操作Bitmap导致性能问题。

实现步骤

  1. 创建与画布尺寸一致的离屏Bitmap
  2. 将当前画布内容绘制到Bitmap
  3. 遍历像素统计擦除区域(baseImage显示区域)的像素数量
  4. 计算擦除百分比并判断是否达标

代码示例

// 在用户停止触摸时触发计算(例如ACTION_UP时)
MotionEvent.ACTION_UP -> {
    val bitmap = Bitmap.createBitmap(size.width.toInt(), size.height.toInt(), Bitmap.Config.ARGB_8888)
    val offscreenCanvas = Canvas(bitmap)

    // 绘制当前画面到离屏Canvas
    with(overlayImageLoaded) {
        offscreenCanvas.draw(size = Size(size.width, size.height))
    }
    clipPath(path = currentPath, clipOp = ClipOp.Intersect) {
        with(baseImageLoaded) {
            offscreenCanvas.draw(size = Size(size.width, size.height))
        }
    }

    // 统计像素
    val pixels = IntArray(bitmap.width * bitmap.height)
    bitmap.getPixels(pixels, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)
    
    // 统计baseImage显示的像素数(需根据你的图像调整颜色判断逻辑)
    var erasedPixelCount = 0
    pixels.forEach { pixel ->
        // 示例:判断像素是否非overlay底色(需替换为你的实际判断逻辑)
        if (pixel != Color.WHITE.toArgb()) {
            erasedPixelCount++
        }
    }

    val totalPixels = bitmap.width * bitmap.height
    val erasedPercentage = (erasedPixelCount.toFloat() / totalPixels) * 100
    val isErasedEnough = erasedPercentage >= 70f
}

方案选择建议

  • 若只需近似判断70%的擦除比例,优先选择方法一,性能开销低、计算高效
  • 若需要像素级精确判断,且图像差异明显,可使用方法二,但需注意在合适时机触发计算,避免性能问题

内容的提问来源于stack exchange,提问作者StuartDTO

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 04:15:40