상단에 있는 이미지를 누르면 카메라 배경에 해당 이미지가 나옵니다. 캡처를 하게되면 그 화면이 캡처되도록 구현했습니다.
그리고 전환 버튼은 카메라의 전면과 후면을 바꿀 수 있게 추가 코딩 해봤습니다. ㅎㅎ
잠깐)
제가 구현한 방식은 Preview 화면 앞에 배경이 투명인 이미지를 추가하여 마치 배경이 있는 카메라 화면인 것 처럼 만들었습니다. Preview 화면에 직접 오버레이 된 방식이 아니므로 캡처 시 카메라 이미지 + 선택한 배경 이미지 를 합성했습니다. 디바이스 별로 카메라 이미지 크기와 배경 이미지 크기가 달라 약간의 오차가 발생할 수 있습니다. 이 부분은 추후 계속 수정해 나가겠습니다. 혹은 다른 방법을 알고 계신 분은 댓글 부탁드립니다.
_MainActivity.kt
버튼 3개를 추가해 줍니다.
btnTransform.setOnClickListener { //전면, 후면 전환시 배경 이미지를 초기화 해줍니다. ivFrameSet.setImageDrawable(null) transformCamera() } ivFrame1.setOnClickListener { ivFrameSet.setImageResource(R.drawable.frame1) myCameraPreview?.setFrameId(R.drawable.frame1)
//파일로 저장 new MyCameraPreview.SaveImageTask().execute(currentData); } };
framId 가 0이 아닌 경우 해당 이미지를 bitmap 변환 후 Canvas 를 통해 이미지를 합성해 줍니다.
이렇게 설정하면 내가 배경으로 지정한 이미지와 함께 화면이 캡처됩니다.
아래는 MyCameraPreview 전체 소스 입니다.
public class MyCameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private final String TAG = "MyTag"; private SurfaceHolder mHolder; private int frameId; private int mCameraID; private Camera mCamera; private Camera.CameraInfo mCameraInfo; private int mDisplayOrientation; public MyCameraPreview(Context context, int cameraId) { super(context); Log.d(TAG, "MyCameraPreview cameraId : " + cameraId); // init frameId zero frameId = 0; // 0 -> CAMERA_FACING_BACK // 1 -> CAMERA_FACING_FRONT mCameraID = cameraId; try { // attempt to get a Camera instance mCamera = Camera.open(mCameraID); } catch (Exception e) { // Camera is not available (in use or does not exist) Log.e(TAG, "Camera is not available"); }
// Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. mHolder = getHolder(); mHolder.addCallback(this); // deprecated setting, but required on Android versions prior to 3.0 mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // get display orientation mDisplayOrientation = ((Activity)context).getWindowManager().getDefaultDisplay().getRotation(); }
public void surfaceCreated(SurfaceHolder holder) { Log.d(TAG, "surfaceCreated"); // retrieve camera's info. Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); Camera.getCameraInfo(mCameraID, cameraInfo); mCameraInfo = cameraInfo; // The Surface has been created, now tell the camera where to draw the preview. try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); } catch (IOException e) { Log.d(TAG, "Error setting camera preview: " + e.getMessage()); } }
public void surfaceDestroyed(SurfaceHolder holder) { Log.d(TAG, "surfaceDestroyed");
// empty. Take care of releasing the Camera preview in your activity. if (mCamera != null) { mCamera.stopPreview(); mCamera.release(); mCamera = null; holder.removeCallback(this); }
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { Log.d(TAG, "surfaceChanged"); // If your preview can change or rotate, take care of those events here. // Make sure to stop the preview before resizing or reformatting it. if (mHolder.getSurface() == null){ // preview surface does not exist Log.e(TAG, "preview surface does not exist"); return; }
// stop preview before making changes try { mCamera.stopPreview(); Log.d(TAG, "Preview stopped."); } catch (Exception e) { // ignore: tried to stop a non-existent preview Log.d(TAG, "Error starting camera preview: " + e.getMessage()); }
// set preview size and make any resize, rotate or // reformatting changes here // start preview with new settings int orientation = calculatePreviewOrientation(mCameraInfo, mDisplayOrientation); mCamera.setDisplayOrientation(orientation); try { mCamera.setPreviewDisplay(mHolder); mCamera.startPreview(); Log.d(TAG, "Camera preview started."); } catch (Exception e) { Log.d(TAG, "Error starting camera preview: " + e.getMessage()); }
}
/** * 안드로이드 디바이스 방향에 맞는 카메라 프리뷰를 화면에 보여주기 위해 계산합니다. */ public int calculatePreviewOrientation(Camera.CameraInfo info, int rotation) { int degrees = 0; switch (rotation) { case Surface.ROTATION_0: degrees = 0; break; case Surface.ROTATION_90: degrees = 90; break; case Surface.ROTATION_180: degrees = 180; break; case Surface.ROTATION_270: degrees = 270; break; }
int result; if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { result = (info.orientation + degrees) % 360; result = (360 - result) % 360; // compensate the mirror } else { // back-facing result = (info.orientation - degrees + 360) % 360; }
/** * 이미지 캡처 시 배경 선택 */ public void setFrameId(int frameId) { this.frameId = frameId; }
/** * 이미지 저장을 위한 콜백 함수 */ private Camera.ShutterCallback shutterCallback = new Camera.ShutterCallback() { public void onShutter() {
} }; private Camera.PictureCallback rawCallback = new Camera.PictureCallback() { public void onPictureTaken(byte[] data, Camera camera) {
} }; private Camera.PictureCallback jpegCallback = new Camera.PictureCallback() { public void onPictureTaken(byte[] data, Camera camera) {
//이미지의 너비와 높이 결정 int w = camera.getParameters().getPictureSize().width; int h = camera.getParameters().getPictureSize().height; int orientation = calculatePreviewOrientation(mCameraInfo, mDisplayOrientation); //Log.d("MyTag","이미지 캡처 시 -> orientation : " + orientation); //byte array 를 bitmap 으로 변환 BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.ARGB_8888; Bitmap bitmap = BitmapFactory.decodeByteArray( data, 0, data.length, options); //이미지를 디바이스 방향으로 회전 Matrix matrix = new Matrix(); /** * 셀카 모드에는 저장 시 좌우 반전을 해줘야 한다. */ if(mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { matrix.setScale(-1,1); }
matrix.postRotate(orientation); bitmap = Bitmap.createBitmap(bitmap, 0, 0, w, h, matrix, true); /** * frameId 가 있는 경우 해당 이미지를 합성해 준다. */ int width = bitmap.getWidth(); int height = bitmap.getHeight(); if(0 != frameId) {
Bitmap bp = BitmapFactory.decodeResource(getContext().getResources(),frameId); Bitmap resizeBp = Bitmap.createScaledBitmap(bp, width, height, true); Canvas canvas = new Canvas(bitmap); canvas.drawBitmap(resizeBp, 0f, 0f,null); }
//bitmap 을 byte array 로 변환 ByteArrayOutputStream stream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream); byte[] currentData = stream.toByteArray(); //파일로 저장 new MyCameraPreview.SaveImageTask().execute(currentData); } }; /** * 이미지 저장을 위한 콜백 클래스 */ private class SaveImageTask extends AsyncTask<byte[], Void, Void> {