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

如何组合使用RenderScript多滤镜配合SeekBar实现参数调节与叠加

问题背景

我想要使用RenderScript滤镜实现如下GIF展示的调节效果:
滤镜调节效果演示

存在问题

所有RenderScript滤镜单独运行均正常,但切换滤镜时会重置位图,无法将多个滤镜的调整参数叠加应用到同一张位图上。

相关代码

Filters类实现:

class Filters(private val context: Context)   {
    private val TAG = BrightnessFilter::class.java.simpleName

    private var rs: RenderScript = RenderScript.create(context)
    private var brightnessScript: ScriptC_brightness = ScriptC_brightness(rs)
    private var contrastScript: ScriptC_contrast = ScriptC_contrast(rs)
    private var saturationFilter: ScriptC_saturation = ScriptC_saturation(rs)
    private var convolution: ScriptIntrinsicConvolve3x3 =
        ScriptIntrinsicConvolve3x3.create(rs, Element.U8_4(rs))
    private var vignetteFilter: ScriptC_vignette = ScriptC_vignette(rs)

    fun setBrightnessX(values: Float, inputImage: Bitmap): Bitmap {
        val outBitmap = Bitmap.createBitmap(inputImage)
        val tempIn = Allocation.createFromBitmap(rs, inputImage)
        val tempOut = Allocation.createFromBitmap(rs, outBitmap)
        brightnessScript.invoke_setBright(values)
        brightnessScript.forEach_brightness(tempIn, tempOut)
        tempOut.copyTo(outBitmap)
        return outBitmap
    }

    fun setContrastX(contrast: Float, inputImage: Bitmap): Bitmap {
        val outBitmap = Bitmap.createBitmap(inputImage)
        val tempIn = Allocation.createFromBitmap(rs, inputImage)
        val tempOut = Allocation.createFromBitmap(rs, outBitmap)
        contrastScript._contrast = contrast
        contrastScript.forEach_contrastness(tempIn, tempOut)
        tempOut.copyTo(outBitmap)
        tempIn.destroy()
        tempOut.destroy()
        return outBitmap
    }

    fun setSaturationX(saturation: Float, inputImage: Bitmap): Bitmap {
        val outBitmap = Bitmap.createBitmap(inputImage)
        val tempIn = Allocation.createFromBitmap(rs, inputImage)
        val tempOut = Allocation.createFromBitmap(rs, outBitmap)
        saturationFilter._saturationValue = saturation
        saturationFilter.forEach_saturation(tempIn, tempOut)
        tempOut.copyTo(outBitmap)
        tempIn.destroy()
        tempOut.destroy()
        return outBitmap
    }

    fun sharpenX(sharpen: Float, bitmap: Bitmap): Bitmap {
        val matrix = floatArrayOf(
            0f, -sharpen, 0f,
            -sharpen, 1 + 4 * sharpen, -sharpen,
            0f, -sharpen, 0f
        )
        return applySharpenConvolveX(bitmap, matrix)
    }

    private fun applySharpenConvolveX(inputImage: Bitmap, coefficients: FloatArray): Bitmap {
        val outBitmap = Bitmap.createBitmap(inputImage)
        val tempIn = Allocation.createFromBitmap(rs, inputImage)
        val tempOut = Allocation.createFromBitmap(rs, outBitmap)
        convolution.setInput(tempIn)
        convolution.setCoefficients(coefficients)
        convolution.forEach(tempOut)
        tempOut.copyTo(outBitmap)
        return outBitmap
    }

    fun vignetteX(values: Float, inputImage: Bitmap): Bitmap {
        vignetteFilter.setScale(values)
        return vignetteFilter.x(inputImage)
    }
}

滑块触发的滤镜调用逻辑:

private fun currentFilter(values: Float): Bitmap {
    return when (_recentTab.value!!) {
        FilterName.BRIGHTNESS -> _filter.value!!.setBrightnessX(values, mInputImage)
        FilterName.CONTRAST -> _filter.value!!.setContrastX(values, mInputImage)
        FilterName.SATURATION -> _filter.value!!.setSaturationX(values, mInputImage)
        FilterName.SHARPEN -> _filter.value!!.sharpenX(values, mInputImage)
        FilterName.VIGNETTE -> _filter.value!!.vignetteX(values, mInputImage)
    }
}

解决方案

核心根因:当前逻辑每次调用滤镜都传入原始图片mInputImage,所有调整都基于原图计算,自然不会叠加前序滤镜的效果。

修复思路

新增全局变量存储所有滤镜的当前调整参数,每次调整滑块时只更新对应参数,再按顺序将所有滤镜叠加应用到原始图上,上一个滤镜的输出作为下一个滤镜的输入,最终生成叠加效果的位图。

代码修改示例

  1. 先定义变量存储所有滤镜的参数,默认值为无效果状态:
// 各滤镜初始默认值:无效果
private var brightnessVal = 0f
private var contrastVal = 1f
private var saturationVal = 1f
private var sharpenVal = 0f
private var vignetteVal = 0f
  1. 修改滑块回调逻辑,仅更新对应参数后统一渲染所有滤镜:
private fun onFilterSliderChange(filterType: FilterName, value: Float) {
    // 仅更新对应滤镜的参数值
    when(filterType) {
        FilterName.BRIGHTNESS -> brightnessVal = value
        FilterName.CONTRAST -> contrastVal = value
        FilterName.SATURATION -> saturationVal = value
        FilterName.SHARPEN -> sharpenVal = value
        FilterName.VIGNETTE -> vignetteVal = value
    }
    // 统一渲染叠加所有滤镜的最终效果,更新ImageView
    val finalBitmap = applyAllFilters(mInputImage)
    targetImageView.setImageBitmap(finalBitmap)
}
  1. 新增统一渲染叠加滤镜的方法:
private fun applyAllFilters(originBitmap: Bitmap): Bitmap {
    var currentBitmap = originBitmap
    // 按顺序逐个应用滤镜,前一个的输出作为后一个的输入
    if (brightnessVal != 0f) {
        currentBitmap = _filter.value!!.setBrightnessX(brightnessVal, currentBitmap)
    }
    if (contrastVal != 1f) {
        currentBitmap = _filter.value!!.setContrastX(contrastVal, currentBitmap)
    }
    if (saturationVal != 1f) {
        currentBitmap = _filter.value!!.setSaturationX(saturationVal, currentBitmap)
    }
    if (sharpenVal != 0f) {
        currentBitmap = _filter.value!!.sharpenX(sharpenVal, currentBitmap)
    }
    if (vignetteVal != 0f) {
        currentBitmap = _filter.value!!.vignetteX(vignetteVal, currentBitmap)
    }
    return currentBitmap
}

性能优化建议

当前的滤镜方法每次调用都会新建Bitmap和Allocation实例,频繁触发会产生内存抖动,可进一步缓存复用RenderScript的Allocation对象,减少内存开销。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 23:18:02