Tạo hiệu ứng cho ảnh trong Android với thư viện GPUImage
1. Giới thiệu GPUImage là một framework khá nổi tiếng trong việc tạo hiệu ứng cho những bức ảnh, nhưng nó lại chỉ hỗ trợ iOS. Thật may, một tổ chức là CyberAgent đã phát triển một thư viện tương tự cho phía Android giúp những người lập trình Android có thêm một công cụ thật hữu ích. Link thư ...
1. Giới thiệu
GPUImage là một framework khá nổi tiếng trong việc tạo hiệu ứng cho những bức ảnh, nhưng nó lại chỉ hỗ trợ iOS. Thật may, một tổ chức là CyberAgent đã phát triển một thư viện tương tự cho phía Android giúp những người lập trình Android có thêm một công cụ thật hữu ích.
Link thư viện: https://github.com/CyberAgent/android-gpuimage
2. Cài đặt
Bạn chỉ cần thêm vào app build.gradle đoạn sau:
repositories { mavenCentral() } dependencies { compile 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.3.0' }
Chú ý: Có những trường hợp app chạy bị lỗi do thiếu thư viện libgpuimage.so native library, bạn cần làm như sau:
- Copy file yuv-decoder.c từ thư viện vào jni folder trong prọect
- Tạo file Android.mk với nội dung như sau:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := gpuimage-library LOCAL_SRC_FILES := yuv-decoder.c include $(BUILD_SHARED_LIBRARY)
- Tạo file Application.mk với nội dung như sau:
APP_ABI := armeabi armeabi-v7a mips x86 APP_PLATFORM := android-9
- Chạy ndk-build
- Copy những thư viện mới sinh ra vào folder jniLibs
3. Tạo một ứng dụng camera với GPUImage
Điểm đặc biệt của GPUImage nữa là nó hỗ trợ Camera Preview, mà có thể đưa hiệu ứng, filter vào từng frame của camera preview. Tôi xin giới thiệu cách tạo 1 Camera Preview sau đây:
- Tạo 1 class CameraHelper, để quản lý việc mở Camera, lấy thông tin của camera,...
public class CameraHelper { private final CameraHelperImpl mImpl; public CameraHelper(final Context context) { if (SDK_INT >= GINGERBREAD) { mImpl = new CameraHelperGB(); } else { mImpl = new CameraHelperBase(context); } } public interface CameraHelperImpl { int getNumberOfCameras(); Camera openCamera(int id); Camera openDefaultCamera(); Camera openCameraFacing(int facing); boolean hasCamera(int cameraFacingFront); void getCameraInfo(int cameraId, CameraInfo2 cameraInfo); } public int getNumberOfCameras() { return mImpl.getNumberOfCameras(); } public Camera openCamera(final int id) { return mImpl.openCamera(id); } public Camera openDefaultCamera() { return mImpl.openDefaultCamera(); } public Camera openFrontCamera() { return mImpl.openCameraFacing(CameraInfo.CAMERA_FACING_FRONT); } public Camera openBackCamera() { return mImpl.openCameraFacing(CameraInfo.CAMERA_FACING_BACK); } public boolean hasFrontCamera() { return mImpl.hasCamera(CameraInfo.CAMERA_FACING_FRONT); } public boolean hasBackCamera() { return mImpl.hasCamera(CameraInfo.CAMERA_FACING_BACK); } public void getCameraInfo(final int cameraId, final CameraInfo2 cameraInfo) { mImpl.getCameraInfo(cameraId, cameraInfo); } public void setCameraDisplayOrientation(final Activity activity, final int cameraId, final Camera camera) { int result = getCameraDisplayOrientation(activity, cameraId); camera.setDisplayOrientation(result); } public int getCameraDisplayOrientation(final Activity activity, final int cameraId) { int rotation = activity.getWindowManager().getDefaultDisplay() .getRotation(); 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; CameraInfo2 info = new CameraInfo2(); getCameraInfo(cameraId, info); if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { result = (info.orientation + degrees) % 360; } else { // back-facing result = (info.orientation - degrees + 360) % 360; } return result; } public static class CameraInfo2 { public int facing; public int orientation; } }
- Ở lớp trên ta thấy có 1 interface là CameraHelpImpl với 2 lớp implement interface đó là CameraHelperBase và CameraHelperGB để dùng cho các bản Android < 2.3 và >= 2.3. Tạo 2 lớp đó như dưới đây:
CameraHelperBase
public class CameraHelperBase implements CameraHelper.CameraHelperImpl { private final Context mContext; public CameraHelperBase(final Context context) { mContext = context; } @Override public int getNumberOfCameras() { return hasCameraSupport() ? 1 : 0; } @Override public Camera openCamera(final int id) { return Camera.open(); } @Override public Camera openDefaultCamera() { return Camera.open(); } @Override public boolean hasCamera(final int facing) { if (facing == CameraInfo.CAMERA_FACING_BACK) { return hasCameraSupport(); } return false; } @Override public Camera openCameraFacing(final int facing) { if (facing == CameraInfo.CAMERA_FACING_BACK) { return Camera.open(); } return null; } @Override public void getCameraInfo(final int cameraId, final CameraHelper.CameraInfo2 cameraInfo) { cameraInfo.facing = Camera.CameraInfo.CAMERA_FACING_BACK; cameraInfo.orientation = 90; } private boolean hasCameraSupport() { return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA); } }
CameraHelperGB
public class CameraHelperGB implements CameraHelperImpl { @Override public int getNumberOfCameras() { return Camera.getNumberOfCameras(); } @Override public Camera openCamera(final int id) { return Camera.open(id); } @Override public Camera openDefaultCamera() { return Camera.open(0); } @Override public boolean hasCamera(final int facing) { return getCameraId(facing) != -1; } @Override public Camera openCameraFacing(final int facing) { return Camera.open(getCameraId(facing)); } @Override public void getCameraInfo(final int cameraId, final CameraInfo2 cameraInfo) { CameraInfo info = new CameraInfo(); Camera.getCameraInfo(cameraId, info); cameraInfo.facing = info.facing; cameraInfo.orientation = info.orientation; } private int getCameraId(final int facing) { int numberOfCameras = Camera.getNumberOfCameras(); CameraInfo info = new CameraInfo(); for (int id = 0; id < numberOfCameras; id++) { Camera.getCameraInfo(id, info); if (info.facing == facing) { return id; } } return -1; } }
- Trong activity chứa camera preview, tạo 1 lớp CameraLoader quản lý việc thiết lập, release, đổi orientation cho Camera bằng cách sử dụng lớp GPUImage
private class CameraLoader { private int mCurrentCameraId = 0; private Camera mCameraInstance; public void onResume() { setUpCamera(mCurrentCameraId); } public void onPause() { releaseCamera(); } public void switchCamera() { releaseCamera(); mCurrentCameraId = (mCurrentCameraId + 1) % mCameraHelper.getNumberOfCameras(); setUpCamera(mCurrentCameraId); } private void setUpCamera(final int id) { mCameraInstance = getCameraInstance(id); Camera.Parameters parameters = mCameraInstance.getParameters(); // TODO adjust by getting supportedPreviewSizes and then choosing List<Camera.Size> list = mCameraInstance.getParameters().getSupportedPreviewSizes(); // the best one for screen size (best fill screen) if (parameters.getSupportedFocusModes().contains( Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) { parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); } mCameraInstance.setParameters(parameters); int orientation = mCameraHelper.getCameraDisplayOrientation( CameraActivity.this, mCurrentCameraId); CameraHelper.CameraInfo2 cameraInfo = new CameraHelper.CameraInfo2(); mCameraHelper.getCameraInfo(mCurrentCameraId, cameraInfo); boolean flipHorizontal = cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT; mGPUImage.setUpCamera(mCameraInstance, orientation, flipHorizontal, false); } /** A safe way to get an instance of the Camera object. */ private Camera getCameraInstance(final int id) { Camera c = null; try { c = mCameraHelper.openCamera(id); } catch (Exception e) { e.printStackTrace(); } return c; } private void releaseCamera() { mGPUImage.deleteImage(); mCameraInstance.setPreviewCallback(null); mCameraInstance.release(); mCameraInstance = null; } }
- Đến đây, việc tạo các class hỗ trợ Camera preview đã hoàn thành, giờ chúng ta chỉ cần tạo 1 GLSurfaceView để hiển thị Camera preview lên đó
<android.opengl.GLSurfaceView
android:id="@+id/surfaceView"
android:layout_awidth="match_parent"
android:layout_height="match_parent"
/>
private GPUImage mGPUImage; private CameraHelper mCameraHelper; private CameraLoader mCamera; ... mGPUImage = new GPUImage(this); mGPUImage.setGLSurfaceView((GLSurfaceView) findViewById(R.id.surfaceView)); mCameraHelper = new CameraHelper(this); mCamera = new CameraLoader();
Vậy là ta đã có 1 ứng dụng Camera preview dùng GPUImage, để thêm hiệu ứng cho camera, ta chỉ cần thêm
mGPUImage.setFilter(new GPUImageFilter...);
4. Demo
Dưới đây là demo CameraPreview sử dụng GPUImage
https://github.com/dzung0709/CameraPreview