Android实现快速Color Splash效果的方法或库推荐请求
嘿,我之前刚好踩过逐像素处理大图的坑,那种卡顿感真的难受!给你几个高效实现Android Color Splash效果的方案,亲测好用:
高效实现Android Color Splash效果的方案
一、首选:使用RenderScript(官方高性能方案)
RenderScript是Android官方提供的高性能计算框架,专门针对像素级、矩阵类的密集型运算优化,能利用GPU或多核CPU加速,处理大图的速度比Java逐像素快5-10倍以上,而且不需要引入第三方库,兼容性也不错(API 11+,低版本可借助支持库)。
步骤1:创建RenderScript脚本文件
在src/main/rs目录下创建ColorSplash.rs(如果没有rs目录就新建一个):
#pragma version(1) #pragma rs java_package_name(com.your.package.name) // 替换成你的包名 rs_allocation inputImage; rs_allocation outputImage; float3 targetColor; float tolerance = 0.2f; // 颜色容差,值越大匹配的颜色范围越广 void root(const uchar4 *v_in, uchar4 *v_out) { // 把输入像素转为0-1的浮点颜色值 float3 inColor = rsUnpackColor8888(*v_in); // 计算与目标颜色的欧氏距离 float diff = distance(inColor, targetColor); if (diff > tolerance) { // 去饱和化:转为灰度 float gray = 0.299f * inColor.r + 0.587f * inColor.g + 0.114f * inColor.b; *v_out = rsPackColorTo8888(gray, gray, gray, inColor.a); } else { // 保留原颜色 *v_out = *v_in; } }
步骤2:Java端调用代码
import android.graphics.Bitmap; import android.graphics.Color; import android.renderscript.Allocation; import android.renderscript.Element; import android.renderscript.RenderScript; import android.renderscript.ScriptC_ColorSplash; public Bitmap applyColorSplash(RenderScript rs, Bitmap originalBitmap, int targetColor) { // 创建输出Bitmap Bitmap outputBitmap = Bitmap.createBitmap( originalBitmap.getWidth(), originalBitmap.getHeight(), originalBitmap.getConfig() ); // 分配RenderScript需要的内存块 Allocation inputAlloc = Allocation.createFromBitmap(rs, originalBitmap); Allocation outputAlloc = Allocation.createFromBitmap(rs, outputBitmap); // 初始化脚本并设置参数 ScriptC_ColorSplash script = new ScriptC_ColorSplash(rs); script.set_inputImage(inputAlloc); script.set_outputImage(outputAlloc); // 将目标颜色转为0-1的浮点数组 float r = Color.red(targetColor) / 255.0f; float g = Color.green(targetColor) / 255.0f; float b = Color.blue(targetColor) / 255.0f; script.set_targetColor(new float[]{r, g, b}); script.set_tolerance(0.3f); // 可根据需求调整容差 // 执行像素处理 script.forEach_root(inputAlloc, outputAlloc); // 将处理后的结果复制到输出Bitmap outputAlloc.copyTo(outputBitmap); // 释放资源,避免内存泄漏 inputAlloc.destroy(); outputAlloc.destroy(); script.destroy(); return outputBitmap; }
二、简化开发:用Glide自定义Transformation
如果你的场景本来就用Glide加载图片,那直接自定义一个Transformation是最省心的选择——Glide会自动处理线程调度、缓存和Bitmap资源管理,不用自己操心。
import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import com.bumptech.glide.load.resource.bitmap.BitmapResource; import com.bumptech.glide.load.resource.bitmap.TransformationUtils; import com.bumptech.glide.load.Transformation; import com.bumptech.glide.load.engine.Resource; public class ColorSplashTransformation implements Transformation<Bitmap> { private final int targetColor; private final float tolerance; public ColorSplashTransformation(int targetColor, float tolerance) { this.targetColor = targetColor; this.tolerance = tolerance; } @Override public Resource<Bitmap> transform(Context context, Resource<Bitmap> resource, int outWidth, int outHeight) { Bitmap source = resource.get(); // 先获取Glide的Bitmap池,复用Bitmap减少内存开销 Bitmap result = TransformationUtils.getBitmapDrawable(context.getResources(), source).getBitmap(); Canvas canvas = new Canvas(result); Paint grayPaint = new Paint(); // 第一步:给整张图去饱和化(转灰度) ColorMatrix grayMatrix = new ColorMatrix(); grayMatrix.setSaturation(0); grayPaint.setColorFilter(new ColorMatrixColorFilter(grayMatrix)); canvas.drawBitmap(source, 0, 0, grayPaint); // 第二步:用PorterDuff模式叠加原颜色的目标区域 Paint colorPaint = new Paint(); colorPaint.setColorFilter(new PorterDuffColorFilter(targetColor, PorterDuff.Mode.SRC_IN)); canvas.drawBitmap(source, 0, 0, colorPaint); return BitmapResource.obtain(result, com.bumptech.glide.Glide.get(context).getBitmapPool()); } @Override public String getId() { // 唯一标识,用于Glide的缓存键 return "ColorSplash:" + targetColor + ":" + tolerance; } }
使用时直接在Glide加载链中添加:
Glide.with(context) .load(imageUrl) .transform(new ColorSplashTransformation(Color.RED, 0.3f)) .into(imageView);
三、应急优化:改进你的逐像素代码
如果暂时不想引入新框架,也可以优化你的原有代码,提升不少速度:
- 一次性获取所有像素数组,避免频繁IO
- 用多线程并行处理
- 用整数运算代替浮点数,避免开根号等耗时操作
import android.graphics.Bitmap; import android.graphics.Color; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.RecursiveAction; public Bitmap applyColorSplashOptimized(Bitmap original, int targetColor, int tolerance) { int width = original.getWidth(); int height = original.getHeight(); Bitmap result = Bitmap.createBitmap(width, height, original.getConfig()); int[] pixels = new int[width * height]; original.getPixels(pixels, 0, width, 0, 0, width, height); // 提前计算目标颜色的RGB值和容差平方(避免循环中重复计算) int targetR = Color.red(targetColor); int targetG = Color.green(targetColor); int targetB = Color.blue(targetColor); int toleranceSq = tolerance * tolerance; // 用平方代替开根号,提升速度 // 用ForkJoinPool并行处理像素数组 ForkJoinPool.commonPool().invoke(new RecursiveAction() { @Override protected void compute() { for (int i = 0; i < pixels.length; i++) { int pixel = pixels[i]; int r = Color.red(pixel); int g = Color.green(pixel); int b = Color.blue(pixel); // 计算颜色距离的平方 int dr = r - targetR; int dg = g - targetG; int db = b - targetB; int distanceSq = dr*dr + dg*dg + db*db; if (distanceSq > toleranceSq) { // 转为灰度 int gray = (int)(0.299*r + 0.587*g + 0.114*b); pixels[i] = Color.argb(Color.alpha(pixel), gray, gray, gray); } } } }); result.setPixels(pixels, 0, width, 0, 0, width, height); return result; }
内容的提问来源于stack exchange,提问作者filipst
相关产品推荐
相关产品推荐

