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

Bitmap指定单一颜色替换咨询:求遍历与全局ColorFilter外的标准方法

嘿,这个问题我做项目的时候也纠结过!确实,普通的ColorFilter要么一调就影响所有像素,要么手动遍历像素在大图上卡得不行,完全不符合我们想要的精准高效需求。下面给你两个更标准化的方案,亲测好用:

高效替换Bitmap指定颜色的两种方案

方案一:用RenderScript做硬件加速的精准替换

RenderScript是Android官方提供的高性能计算框架,专门处理这种像素级操作,比Java/Kotlin层手动遍历快N倍,尤其是处理大图的时候优势拉满。

步骤&代码

  1. 先写一个RenderScript内核脚本(新建ReplaceColor.rs文件放在src/main/rs目录下):
#pragma version(1)
#pragma rs java_package_name(com.your.package.name) // 替换成你的包名

rs_allocation inputImage;
rs_allocation outputImage;
int targetColor;
int replacementColor;
int colorTolerance = 20; // 颜色容差,可根据需求调整

void root(const uchar4 *v_in, uchar4 *v_out) {
    // 提取当前像素的RGBA值
    int currentR = v_in->r;
    int currentG = v_in->g;
    int currentB = v_in->b;
    int currentA = v_in->a;

    // 提取目标颜色的RGBA分量
    int targetR = rsUnpackColorRed(targetColor);
    int targetG = rsUnpackColorGreen(targetColor);
    int targetB = rsUnpackColorBlue(targetColor);
    int targetA = rsUnpackColorAlpha(targetColor);

    // 判断是否匹配(带容差,避免颜色细微差异漏判)
    bool isMatch = abs(currentR - targetR) <= colorTolerance
            && abs(currentG - targetG) <= colorTolerance
            && abs(currentB - targetB) <= colorTolerance
            && abs(currentA - targetA) <= colorTolerance;

    if (isMatch) {
        // 替换颜色
        *v_out = rsPackColorToRGBA8(replacementColor);
    } else {
        // 保留原像素
        *v_out = *v_in;
    }
}
  1. 在Kotlin/Java代码中调用这个脚本:
// 初始化RenderScript
val rs = RenderScript.create(context)
// 创建输入输出的内存分配对象
val inputAlloc = Allocation.createFromBitmap(rs, originalBitmap)
val outputAlloc = Allocation.createTyped(rs, inputAlloc.type)

// 加载自定义脚本
val colorReplaceScript = ScriptC_ReplaceColor(rs)
// 设置参数
colorReplaceScript._inputImage = inputAlloc
colorReplaceScript._targetColor = targetColor // 比如Color.RED
colorReplaceScript._replacementColor = replacementColor // 比如Color.BLUE
colorReplaceScript._colorTolerance = 20 // 可按需调整

// 执行像素替换
colorReplaceScript.forEach_root(inputAlloc, outputAlloc)

// 将结果复制到新Bitmap
val resultBitmap = Bitmap.createBitmap(originalBitmap.width, originalBitmap.height, originalBitmap.config)
outputAlloc.copyTo(resultBitmap)

// 释放资源,避免内存泄漏
inputAlloc.destroy()
outputAlloc.destroy()
colorReplaceScript.destroy()
rs.destroy()

这个方案支持颜色容差,能处理相近颜色的替换,而且硬件加速加持下,性能比手动遍历好太多。

方案二:用Canvas+PorterDuffXfermode做蒙版混合

如果不想写RenderScript,这个纯Canvas/Paint的方案也能搞定,适合简单的精准颜色替换场景:

代码示例(Kotlin)

// 1. 创建目标颜色的蒙版:只保留原Bitmap中目标颜色的区域
val maskBitmap = Bitmap.createBitmap(originalBitmap.width, originalBitmap.height, Bitmap.Config.ARGB_8888)
val maskCanvas = Canvas(maskBitmap)
val maskPaint = Paint().apply {
    // 用SRC_IN模式过滤出目标颜色区域
    colorFilter = PorterDuffColorFilter(targetColor, PorterDuff.Mode.SRC_IN)
}
maskCanvas.drawBitmap(originalBitmap, 0f, 0f, maskPaint)

// 2. 创建替换颜色层:只在蒙版区域绘制替换颜色
val replacementLayer = Bitmap.createBitmap(originalBitmap.width, originalBitmap.height, Bitmap.Config.ARGB_8888)
val replacementCanvas = Canvas(replacementLayer)
val replacementPaint = Paint().apply {
    color = replacementColor
    // 只在蒙版的不透明区域绘制颜色
    xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
}
replacementCanvas.drawBitmap(maskBitmap, 0f, 0f, replacementPaint)

// 3. 混合原Bitmap和替换层,得到最终结果
val resultBitmap = Bitmap.createBitmap(originalBitmap.width, originalBitmap.height, originalBitmap.config)
val resultCanvas = Canvas(resultBitmap)
resultCanvas.drawBitmap(originalBitmap, 0f, 0f, null)
val blendPaint = Paint().apply {
    // 把替换层叠加到原Bitmap的对应区域
    xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)
}
resultCanvas.drawBitmap(replacementLayer, 0f, 0f, blendPaint)

// 回收临时Bitmap,释放内存
maskBitmap.recycle()
replacementLayer.recycle()

这个方案不用额外写脚本,代码更简洁,但如果需要颜色容差的话,不如RenderScript灵活。

总结

  • 追求高性能+带容差的精准替换:选RenderScript,大图场景必备。
  • 只是简单的精准颜色替换:用Canvas+PorterDuffXfermode更快捷。

另外别忘了回收临时Bitmap,避免内存溢出哦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:31:27