前言
最近有客户反馈,camera2相机切换时,会黑屏一段时间很不美观,影响体验,客户要求能够像小米,华为等品牌机一样,中间有一个模糊的效果。
这个问题由于关联问题比较多,包括,录像,照相,半屏预览,全屏预览,第三方应用调用,以及google GMS 测试的问题。最后还是和客户沟通掉了,不做修改。
但是,这里我们需要知道,相机模糊的原理。
实现步骤
1.点击切换时,获取相机预览的一帧。
2.获取的帧,转为为bitmap
3.在camera surfaceview 之上添加view ,并setBackground 为步骤2中处理的bitmap
具体实现
1.获取预览帧
mCameraDevice.setOneShotPreviewCallback(mHandler, new CameraManager.CameraPreviewDataCallback() {
@TargetApi(Build.VERSION_CODES.FROYO)
@Override
public void onPreviewFrame(byte[] data, CameraProxy camera) {
Log.d(TAG,"wangjicong onPreviewFrame");
Camera.Size size = camera.getParameters().getPreviewSize();
Log.d(TAG, "wangjicong size, width = " + size.width + " height = " + size.height);
mYuvImage = null;
mYuvImage = new YuvImage(data, ImageFormat.NV21, size.width, size.height, null);
通过setOneShotPreviewCallback 获取data 帧数据,由于预览数据时Yuv的,需要需要转化为得到YuvImage
2.通过 YuvImage 得到bitmap
ByteArrayOutputStream stream = new ByteArrayOutputStream();
//Log.d(TAG,"wangjicong blur bitmap size.width = "+size.width+" size.height = "+size.height);
mYuvImage.compressToJpeg(new Rect(0, 0, mYuvImage.getWidth(), mYuvImage.getHeight()), 40, stream);
Bitmap bitmap = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size());
由于我们得到的bitmap,旋转方向和我们手机屏时不同的,以及前摄情况下,bitmap存在镜像,需要bitmap镜像处理。
private Bitmap rotateBitmap(Bitmap origin, int rotate) {
if (origin == null) {
return null;
}
int width = origin.getWidth();
int height = origin.getHeight();
Matrix matrix = new Matrix();
matrix.setRotate(rotate);
if (rotate==-90){
matrix.postScale(-1, 1);
}
Bitmap newBM = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);
if (newBM.equals(origin)) {
return newBM;
}
origin.recycle();
return newBM;
}
3.模糊处理
将我们步骤2中的旋转,镜像处理之后的bitmap 做模糊处理。
private static final float SCALE_DEGREE = 0.4f;
private static final float BLUR_RADIUS = 25f;
private HandlerThread mBlurHandlerThread;
private Handler mBlurHandler;
private YuvImage mYuvImage;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public static Bitmap blur(Bitmap bitmap,Context context) {
//计算图片缩小的长宽
int width = Math.round(bitmap.getWidth()*SCALE_DEGREE);
int height = Math.round(bitmap.getHeight() * SCALE_DEGREE);
//将缩小后的图片作为预渲染的图片
Bitmap inputBitmap = Bitmap.createScaledBitmap(bitmap, width, height, false);
//创建一张渲染后的输入图片
Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);
//创建RenderScript内核对象
RenderScript renderScript = RenderScript.create(context);
//创建一个模糊效果的RenderScript的工具对象
ScriptIntrinsicBlur scriptIntrinsicBlur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));
/**
* 由于RenderScript并没有使用VM来分配内存,所以需要使用Allocation类来创建和分配内存空间。
* 创建Allocation对象的时候其实内存是空的,需要使用copyTo()将数据填充进去。
*/
Allocation inputAllocation = Allocation.createFromBitmap(renderScript, inputBitmap);
Allocation outputAllocation = Allocation.createFromBitmap(renderScript, outputBitmap);
//设置渲染的模糊程度,25f是最大模糊度
scriptIntrinsicBlur.setRadius(BLUR_RADIUS);
//设置ScriptIntrinsicBlur对象的输入内存
scriptIntrinsicBlur.setInput(inputAllocation);
//将ScriptIntrinsicBlur输出数据保存到输出内存中
scriptIntrinsicBlur.forEach(outputAllocation);
//将数据填充到Allocation中
outputAllocation.copyTo(outputBitmap);
return outputBitmap;
}
备注
由于bitmap处理比较耗时,这里定义了一个private HandlerThread mBlurHandlerThread; 来处理。