动态高斯模糊
对SurfaceView实现动态高斯模糊
背景
我是做车机端开发的,目前导航应用里面要实现一个功能,当有view覆盖在主图的上面的时候,要对主图覆盖区域
进行高斯模糊操作。
效果如下
20220613-194016
实现
我当前的系统版本Android 11, Android 12之后系统是支持基于view进行高斯模糊,但是Android 11 及之前的版本还是需
要自己找方法。
目前网上给的方法基本都是静态的,对于单个图片进行高斯模糊,或者动态的高斯模糊也是只能对当前区域进行不断的
截图,然后进行高斯模糊,但是这里有个问题,你要考虑性能,不断截图是特别消耗cpu的,还有一种就是用网上已有的
对于图片进行模糊操作进行修改。
如下
public class GlassBlurView extends View {
private RenderScript mRenderScript = null;
private ScriptIntrinsicBlur mBlurScript = null;
private Allocation mBlurInput = null, mBlurOutput = null;
private Bitmap mBitmapToBlur = null, mBlurredBitmap = null;
private int mOverlayColor = 0x8C000000;
private BlurThread mBlurThread = null;
private boolean stopThread = false;
private Paint mPaint;
private final Rect mRectSrc = new Rect(), mRectDst = new Rect();
private final int MSG_MAP_BITMAP = 1;
private final static String TAG = GlassBlurView.class.getSimpleName();
public GlassBlurView(Context context) {
super(context);
}
public GlassBlurView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
}
// 第一步
// 这几个参数的意思
// int left, int top, int right, int bottom 就是你要对surfaceView(主图),做高斯模糊的区域,
// Application context 就是应用的context
// SurfaceView view 就是继承自surfaceView的主图,也可以是各种基于surfaceView的类型
public void showGlassBlurView(int left, int top, int right, int bottom, Application context, SurfaceView view) {
mBlurThread = new BlurThread(left, top, right, bottom, context, view);
mBlurThread.setName("GlassBlurViewThread");
mBlurThread.start();
}
private void hideGlassBlurView() {
if (!stopThread) {
stopThread = true;
}
if (null != mBlurThread) {
mBlurThread.interrupt();
mBlurThread = null;
}
release();
}
// 第四步
// 这里就是用网上别人做的高斯模糊的轮子了
private void convertGlassblurBitmap(Context context, Bitmap originBitmap, int radius) {
if (mRenderScript == null) {
try {
mRenderScript = RenderScript.create(context);
mBlurScript = ScriptIntrinsicBlur.create(mRenderScript, Element.U8_4(mRenderScript));
} catch (android.renderscript.RSRuntimeException e) {
release();
}
// 这里对获取的bitmap进行不同比例的操作
int width = (int) Math.round(originBitmap.getWidth() * 0.4);
int height = (int) Math.round(originBitmap.getHeight() * 0.4);
mBitmapToBlur = Bitmap.createScaledBitmap(originBitmap, width, height, false);
mBlurredBitmap = Bitmap.createBitmap(mBitmapToBlur);
mBlurScript.setRadius(radius);
mBlurInput = Allocation.createFromBitmap(mRenderScript, mBitmapToBlur,
Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
mBlurOutput = Allocation.createTyped(mRenderScript, mBlurInput.getType());
mBlurInput.copyFrom(mBitmapToBlur);
mBlurScript.setInput(mBlurInput);
mBlurScript.forEach(mBlurOutput);
mBlurOutput.copyTo(mBlurredBitmap);
// 这个方法就会调用到view的draw()方法。告诉view进行重新绘制。不懂的自己去看Android View
//的绘制
invalidate();
}
}
public class BlurThread extends Thread {
private Handler.Callback mCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (null != msg) {
switch (msg.what) {
case 1: {
convertGlassblurBitmap(context, (Bitmap) msg.obj, 20);
}
}
}
return true;
}
};
private Handler mHandler = new Handler(mCallback);
private int left;
private int top;
private int right;
private int bottom;
private Application context;
private SurfaceView view;
private Bitmap mScreenBitmap;
private void sendMessage(int what, Object obj) {
Message msg = Message.obtain();
msg.obj = obj;
msg.what = what;
this.mHandler.sendMessage(msg);
}
public BlurThread(int left, int top, int right, int bottom, Application context, SurfaceView view) {
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
this.context = context;
this.view = view;
}
@Override
public void run() {
//子线程
// 第二步
while (!stopThread) {
Log.d("lisiwei", "开启了线程1 + isInterrupted = " + this.isInterrupted());
if (mScreenBitmap == null) {
mScreenBitmap = Bitmap.createBitmap(right - left, bottom - top, Bitmap.Config.ARGB_8888);
}
Rect tempRect = new Rect(left, top, right, bottom);
// 这里调用是Android原生的接口,主要是从缓存中获取surfaceView数据转成bitmap给出来。request是个重载函数,有基于view和surfaceView 不同参的。
PixelCopy.request(view, tempRect, mScreenBitmap, new PixelCopy.OnPixelCopyFinishedListener() {
@Override
public void onPixelCopyFinished(int copyResult) {
if (PixelCopy.SUCCESS == copyResult) {
// 第三步
// 在开启的线程里面获取SurfaceView变成了bitmap
sendMessage(MSG_MAP_BITMAP, mScreenBitmap);
} else {
Log.d(TAG, "request scapy mapview failed");
}
}
}, mHandler);
try {
// 这里控制去获取缓存中surfaceView内容的时间,根据性能要求去修改
mBlurThread.sleep(150);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
mScreenBitmap.recycle();
mHandler.removeCallbacksAndMessages(null);
mScreenBitmap = null;
mHandler = null;
}
}
@Override
protected void onDetachedFromWindow() {
Log.d(TAG, " onDetachedFromWindow -> start");
hideGlassBlurView();
super.onDetachedFromWindow();
}
// 第五步
@Override
public void draw(Canvas canvas) {
drawBlurredBitmap(canvas, mBlurredBitmap, mOverlayColor);
super.draw(canvas);
}
@Override
protected void onDraw (Canvas canvas){
super.onDraw(canvas);
}
private void drawBlurredBitmap (Canvas canvas, Bitmap blurredBitmap,int overlayColor){
if (blurredBitmap != null) {
mRectSrc.right = blurredBitmap.getWidth();
mRectSrc.bottom = blurredBitmap.getHeight();
mRectDst.right = getWidth();
mRectDst.bottom = getHeight();
// 第六步
canvas.drawBitmap(blurredBitmap, mRectSrc, mRectDst, null);
}
if (null != mPaint) {
// 在做完高斯模糊的surfaceView上面叠加颜色
mPaint.setColor(overlayColor);
canvas.drawRect(mRectDst, mPaint);
}
release();
}
private void release() {
if (mBitmapToBlur != null) {
mBitmapToBlur.recycle();
mBitmapToBlur = null;
}
if (mBlurredBitmap != null) {
mBlurredBitmap.recycle();
mBlurredBitmap = null;
}
if (mBlurInput != null) {
mBlurInput.destroy();
mBlurInput = null;
}
if (mBlurOutput != null) {
mBlurOutput.destroy();
mBlurOutput = null;
}
if (mBlurScript != null) {
mBlurScript.destroy();
mBlurScript = null;
}
if (mRenderScript != null) {
mRenderScript.destroy();
mRenderScript = null;
}
if (null != mPaint) {
mPaint = null;
}
}
}
用法如下直接在布局文件xml里面直接使用
注意事项
1、上面的GlassBlurView 的 showGlassBlurView()方法要在调用的时候传入相应的参数。
2、高斯模糊半径,值越大越模糊,0<r<=25,在convertGlassblurBitmap()方法的第三个参数修改
3、捕获surfaceView之后做的bitmap,进行更模糊的操作,去修改图片比例,在
convertGlassblurBitmap()方法的
int width = (int) Math.round(originBitmap.getWidth() * 0.4);
int height = (int) Math.round(originBitmap.getHeight() * 0.4); 进行操作
4、可以修改获取surfaceView缓存数据的间隔时间,mBlurThread.sleep(150);