如何组合使用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,所有调整都基于原图计算,自然不会叠加前序滤镜的效果。
修复思路
新增全局变量存储所有滤镜的当前调整参数,每次调整滑块时只更新对应参数,再按顺序将所有滤镜叠加应用到原始图上,上一个滤镜的输出作为下一个滤镜的输入,最终生成叠加效果的位图。
代码修改示例
- 先定义变量存储所有滤镜的参数,默认值为无效果状态:
// 各滤镜初始默认值:无效果 private var brightnessVal = 0f private var contrastVal = 1f private var saturationVal = 1f private var sharpenVal = 0f private var vignetteVal = 0f
- 修改滑块回调逻辑,仅更新对应参数后统一渲染所有滤镜:
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) }
- 新增统一渲染叠加滤镜的方法:
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
相关产品推荐
相关产品推荐

