前言
最近有客戶反饋,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; 來處理。