在Android Camera SurfaceView 預(yù)覽拍照一節(jié)中( 鏈接: http://www.itdecent.cn/p/9e0f3fc5a3b4 ),我們介紹了怎樣使用SurfaceView 預(yù)覽Camera中的數(shù)據(jù),但如果要對預(yù)覽幀進行操作,比如濾鏡、變形等實時操作,則需要引入OpenGLES對SurfaceView 中的Surface進行操作。
在實現(xiàn)之前,我們來比較一下SurfaceView、GLSurfaceView、TextureView吧。
GLSurfaceView是Google封裝的繼承于SurfaceView的封裝了EGLContext、EGLDisplay、EGLSurface的View,使用起來相對方便很多,但是如果要定制更多功能就有點力不從心了,比如我需要使用多個EGLSurface,通過EGLContext上下文進行共享的話,GLSurfaceView實現(xiàn)起來就有點麻煩了,GLSurfaceView 是封裝好OpenGLES單線程渲染的View,多線程渲染實現(xiàn)起來有難度,這時候就沒SurfaceView好用了。TextureView是4.0之后才出的,相比GLSurfaceView,除了省電之外,GLSurfaceView該有的功能,Android5.0之前TextureView在View hierachy層進行繪制的,Android5.0以后引入了單獨的渲染線程之后,TextureView是在渲染線程中進行的,TextureView 也可以實現(xiàn)SurfaceView + SurfaceTexture的功能,實現(xiàn)起來相對麻煩些,流程大體一致。三者的詳細分析請參考以下這篇文章,寫得比較詳細,我這里就不詳細介紹了:
http://blog.csdn.net/jinzhuojun/article/details/44062175
另外,如果是使用opengles 做3D渲染的話,個人建議使用SurfaceView,這是從功耗方面考慮的。性能上SurfaceView、GLSurfaceView、TextureView并無明顯的差距
好了,我們接下來看看如何實現(xiàn)該功能:
1、首先自定義一個SurfaceView:
public class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "CameraSurfaceView";
public CameraSurfaceView(Context context) {
super(context);
init();
}
public CameraSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CameraSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
getHolder().addCallback(this);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
CameraDrawer.INSTANCE.surfaceCreated(holder);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
CameraDrawer.INSTANCE.surfacrChanged(width, height);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
CameraDrawer.INSTANCE.surfaceDestroyed();
}
}
自定義的SurfaceView跟上一篇文章實現(xiàn)方式一致,只是在SurfaceHolder.Callback回調(diào)方法中引入了CameraDrawer。這個CameraDrawer是實現(xiàn)OpenGLES繪制Camera預(yù)覽數(shù)據(jù)的最核心的類,我們來看看CameraDrawer是如何實現(xiàn)的吧:
CameraDrawer 采用enum實現(xiàn)的單例,關(guān)于單例模式的實現(xiàn)方式一般情況下有七種,這里采用的是Effective Java作者Josh Bloch 提倡的方式,采用enum單例模式不僅能避免多線程同步問題,還防止反序列化重新創(chuàng)建新對象。只是enum是Java1.5之后才加入的特性,這種寫法估計比較少人用,這里也可以采用雙重校驗鎖的方式實現(xiàn),這里不做談?wù)摗?br>
創(chuàng)建和銷毀HandlerThread 和 Handler的方法:
/**
* 創(chuàng)建HandlerThread和Handler
*/
private void create() {
mHandlerThread = new HandlerThread("CameraDrawer Thread");
mHandlerThread.start();
mDrawerHandler = new CameraDrawerHandler(mHandlerThread.getLooper());
mDrawerHandler.sendEmptyMessage(CameraDrawerHandler.MSG_INIT);
loopingInterval = CameraUtils.DESIRED_PREVIEW_FPS;
}
/**
* 銷毀當(dāng)前持有的Looper 和 Handler
*/
public void destory() {
// Handler不存在時,需要銷毀當(dāng)前線程,否則可能會出現(xiàn)重新打開不了的情況
if (mDrawerHandler == null) {
if (mHandlerThread != null) {
mHandlerThread.quitSafely();
try {
mHandlerThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
mHandlerThread = null;
}
return;
}
synchronized (mSynOperation) {
mDrawerHandler.sendEmptyMessage(CameraDrawerHandler.MSG_DESTROY);
mHandlerThread.quitSafely();
try {
mHandlerThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
mHandlerThread = null;
mDrawerHandler = null;
}
}
如果不熟悉HandlerThread,可以自己找一下資料了解HandlerThread 的源碼實現(xiàn)。本質(zhì)上HandlerThread也是一個Thread,只不過是擁有自己的Looper,創(chuàng)建Handler時,可以綁定該Thread的Looper,這樣就能夠在同一個線程內(nèi)處理消息。如果不使用HandlerThread,想要在子線程里面處理消息,你需要通過自定義Thread,然后通過Looper.prepare()、Looper.loop()來創(chuàng)建綁定到Looper。至于手動調(diào)用Looper怎么實現(xiàn),大家可以自己找一下資料,這里就不錯詳細介紹了。接下來就是暴露出給外界調(diào)用的一些邏輯方法,主要是與Surfaceholder回調(diào)綁定的方法、開始預(yù)覽、停止預(yù)覽、錄制、停止錄制、拍照、銷毀資源等邏輯方法,這些方法通過調(diào)用CameraDrawerHandler傳遞消息進行處理:
public void surfaceCreated(SurfaceHolder holder) {
create();
if (mDrawerHandler != null) {
mDrawerHandler.sendMessage(mDrawerHandler
.obtainMessage(CameraDrawerHandler.MSG_SURFACE_CREATED, holder));
}
}
public void surfacrChanged(int width, int height) {
if (mDrawerHandler != null) {
mDrawerHandler.sendMessage(mDrawerHandler
.obtainMessage(CameraDrawerHandler.MSG_SURFACE_CHANGED, width, height));
}
startPreview();
}
public void surfaceDestroyed() {
stopPreview();
if (mDrawerHandler != null) {
mDrawerHandler.sendMessage(mDrawerHandler
.obtainMessage(CameraDrawerHandler.MSG_SURFACE_DESTROYED));
}
destory();
}
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
if (mDrawerHandler != null) {
mDrawerHandler.addNewFrame();
}
}
/**
* 開始預(yù)覽
*/
public void startPreview() {
if (mDrawerHandler == null) {
return;
}
synchronized (mSynOperation) {
mDrawerHandler.sendMessage(mDrawerHandler
.obtainMessage(CameraDrawerHandler.MSG_START_PREVIEW));
synchronized (mSyncIsLooping) {
isPreviewing = true;
}
}
}
/**
* 停止預(yù)覽
*/
public void stopPreview() {
if (mDrawerHandler == null) {
return;
}
synchronized (mSynOperation) {
mDrawerHandler.sendMessage(mDrawerHandler
.obtainMessage(CameraDrawerHandler.MSG_STOP_PREVIEW));
synchronized (mSyncIsLooping) {
isPreviewing = false;
}
}
}
/**
* 改變Filter類型
*/
public void changeFilterType(FilterType type) {
if (mDrawerHandler == null) {
return;
}
synchronized (mSynOperation) {
mDrawerHandler.sendMessage(mDrawerHandler
.obtainMessage(CameraDrawerHandler.MSG_FILTER_TYPE, type));
}
}
/**
* 更新預(yù)覽大小
* @param width
* @param height
*/
public void updatePreview(int width, int height) {
if (mDrawerHandler == null) {
return;
}
synchronized (mSynOperation) {
mDrawerHandler.sendMessage(mDrawerHandler
.obtainMessage(CameraDrawerHandler.MSG_UPDATE_PREVIEW, width, height));
}
}
/**
* 開始錄制
*/
public void startRecording() {
if (mDrawerHandler == null) {
return;
}
synchronized (mSynOperation) {
mDrawerHandler.sendMessage(mDrawerHandler
.obtainMessage(CameraDrawerHandler.MSG_START_RECORDING));
}
}
/**
* 停止錄制
*/
public void stopRecording() {
if (mDrawerHandler == null) {
return;
}
synchronized (mSynOperation) {
mDrawerHandler.sendEmptyMessage(CameraDrawerHandler.MSG_STOP_RECORDING);
synchronized (mSyncIsLooping) {
isRecording = false;
}
}
}
/**
* 拍照
*/
public void takePicture() {
if (mDrawerHandler == null) {
return;
}
synchronized (mSynOperation) {
// 發(fā)送拍照命令
mDrawerHandler.sendMessage(mDrawerHandler
.obtainMessage(CameraDrawerHandler.MSG_TAKE_PICTURE));
}
}
好了,我們來看看CameraDrawerHandler是怎樣的。CameraDrawerHandler 是一個Handler,主要用來處理HandlerThread中Looper 的消息,其整體實現(xiàn)如下:
private class CameraDrawerHandler extends Handler {
private static final String TAG = "CameraDrawer";
static final int MSG_SURFACE_CREATED = 0x001;
static final int MSG_SURFACE_CHANGED = 0x002;
static final int MSG_FRAME = 0x003;
static final int MSG_FILTER_TYPE = 0x004;
static final int MSG_RESET = 0x005;
static final int MSG_SURFACE_DESTROYED = 0x006;
static final int MSG_INIT = 0x007;
static final int MSG_DESTROY = 0x008;
static final int MSG_START_PREVIEW = 0x100;
static final int MSG_STOP_PREVIEW = 0x101;
static final int MSG_UPDATE_PREVIEW = 0x102;
static final int MSG_START_RECORDING = 0x200;
static final int MSG_STOP_RECORDING = 0x201;
static final int MSG_RESET_BITRATE = 0x300;
static final int MSG_TAKE_PICTURE = 0x400;
// EGL共享上下文
private EglCore mEglCore;
// EGLSurface
private WindowSurface mDisplaySurface;
// CameraTexture對應(yīng)的Id
private int mTextureId;
private SurfaceTexture mCameraTexture;
// 矩陣
private final float[] mMatrix = new float[16];
// 視圖寬高
private int mViewWidth, mViewHeight;
// 預(yù)覽圖片大小
private int mImageWidth, mImageHeight;
// 更新幀的鎖
private final Object mSyncFrameNum = new Object();
private final Object mSyncTexture = new Object();
private int mFrameNum = 0;
private boolean hasNewFrame = false;
public boolean dropNextFrame = false;
private boolean isTakePicture = false;
private boolean mSaveFrame = false;
private int mSkipFrame = 0;
private CameraFilter mCameraFilter;
private BaseImageFilter mFilter;
public CameraDrawerHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
// 初始化
case MSG_INIT:
break;
// 銷毀
case MSG_DESTROY:
break;
// surfacecreated
case MSG_SURFACE_CREATED:
onSurfaceCreated((SurfaceHolder)msg.obj);
break;
// surfaceChanged
case MSG_SURFACE_CHANGED:
onSurfaceChanged(msg.arg1, msg.arg2);
break;
// surfaceDestroyed;
case MSG_SURFACE_DESTROYED:
onSurfaceDestoryed();
break;
// 幀可用(考慮同步的問題)
case MSG_FRAME:
drawFrame();
break;
case MSG_FILTER_TYPE:
setFilter((FilterType) msg.obj);
break;
// 重置
case MSG_RESET:
break;
// 開始預(yù)覽
case MSG_START_PREVIEW:
break;
// 停止預(yù)覽
case MSG_STOP_PREVIEW:
break;
// 更新預(yù)覽大小
case MSG_UPDATE_PREVIEW:
synchronized (mSyncIsLooping) {
mViewWidth = msg.arg1;
mViewHeight = msg.arg2;
}
break;
// 開始錄制
case MSG_START_RECORDING:
break;
// 停止錄制
case MSG_STOP_RECORDING:
break;
// 重置bitrate(錄制視頻時使用)
case MSG_RESET_BITRATE:
break;
// 拍照
case MSG_TAKE_PICTURE:
isTakePicture = true;
break;
default:
throw new IllegalStateException("Can not handle message what is: " + msg.what);
}
}
}
我們看到CameraDrawerHandler 主要是用來處理各種消息的,因此這里的消息定義得非常非常多。從初始化、SurfaceHolder回調(diào)方法處理實體、幀可用、繪制濾鏡、開始錄制、停止錄制、更新預(yù)覽大小、拍照等處理實體。Handler 接受消息后,需要對這些消息進行處理。下面介紹接收到消息后的處理方法。
SurfaceHolder回調(diào)方法處理實體:
private void onSurfaceCreated(SurfaceHolder holder) {
mEglCore = new EglCore(null, EglCore.FLAG_RECORDABLE);
mDisplaySurface = new WindowSurface(mEglCore, holder.getSurface(), false);
mDisplaySurface.makeCurrent();
if (mCameraFilter == null) {
mCameraFilter = new CameraFilter();
}
mTextureId = createTextureOES();
mCameraTexture = new SurfaceTexture(mTextureId);
mCameraTexture.setOnFrameAvailableListener(CameraDrawer.this);
CameraUtils.openFrontalCamera(CameraUtils.DESIRED_PREVIEW_FPS);
calculateImageSize();
mCameraFilter.onInputSizeChanged(mImageWidth, mImageHeight);
mFilter = FilterManager.getFilter(FilterType.SATURATION);
// 禁用深度測試和背面繪制
GLES30.glDisable(GLES30.GL_DEPTH_TEST);
GLES30.glDisable(GLES30.GL_CULL_FACE);
}
/**
* 計算imageView 的寬高
*/
private void calculateImageSize() {
Camera.Size size = CameraUtils.getPreviewSize();
Camera.CameraInfo info = CameraUtils.getCameraInfo();
if (size != null) {
if (info.orientation == 90 || info.orientation == 270) {
mImageWidth = size.height;
mImageHeight = size.width;
} else {
mImageWidth = size.width;
mImageHeight = size.height;
}
} else {
mImageWidth = 0;
mImageHeight = 0;
}
}
/**
* 創(chuàng)建OES 類型的Texture
* @return
*/
private int createTextureOES() {
return GlUtil.createTextureObject(GLES11Ext.GL_TEXTURE_EXTERNAL_OES);
}
我們可以看到在onSurfaceCreated中主要是創(chuàng)建了EglCore 和 WindowSurface,打開相機并設(shè)置期望的幀率,禁用深度和背面繪制功能。這里的EglCore 和WidnowsSurface 來自項目Grafika( 項目地址:https://github.com/google/grafika/ ),Grafika是Google工程師業(yè)余的Demo,介紹了OpenGLES 在SurfaceView、GLSurfaceView、TextureView的各種用法,其中EglCore 和WindowSurface主要是封裝了EGLDisplay、EGLContext 和 EGLSurface,使用起來比較方便,這里就直接引用了。calculateImageSize方法主要是用來計算filter應(yīng)該渲染的大小,返回的是實際的預(yù)覽圖像的size,這里需要注意的是,SurfaceView 顯示和預(yù)覽圖像的Size 并不一定是大小一致的。并且,這里需要根據(jù)相機的想轉(zhuǎn)角度做一層處理,將寬度和高度交換。
然后我們再看看onSurfaceChanged里面的內(nèi)容:
private void onSurfaceChanged(int width, int height) {
mViewWidth = width;
mViewHeight = height;
onFilterChanged();
CameraUtils.startPreviewTexture(mCameraTexture);
}
/**
* 濾鏡或視圖發(fā)生變化時調(diào)用
*/
private void onFilterChanged() {
mCameraFilter.onDisplayChanged(mViewWidth, mViewHeight);
if (mFilter != null) {
mCameraFilter.initCameraFramebuffer(mImageWidth, mImageHeight);
} else {
mCameraFilter.destroyFramebuffer();
}
}
在onSurfaceChanged回調(diào)中調(diào)整Filter,這里主要是處理相機的顯示Size和是否需要渲染到Framebuffer 中。
接下來我們看看幀可用時,以及繪制幀的具體方法:
/**
* 繪制幀
*/
private void drawFrame() {
mDisplaySurface.makeCurrent();
synchronized (mSyncFrameNum) {
synchronized (mSyncTexture) {
if (mCameraTexture != null) {
// 如果存在新的幀,則更新幀
while (mFrameNum != 0) {
mCameraTexture.updateTexImage();
--mFrameNum;
// 是否舍棄下一幀
if (!dropNextFrame) {
hasNewFrame = true;
} else {
dropNextFrame = false;
hasNewFrame = false;
}
}
} else {
return;
}
}
}
mCameraTexture.getTransformMatrix(mMatrix);
if (mFilter == null) {
mCameraFilter.drawFrame(mTextureId, mMatrix);
} else {
int id = mCameraFilter.drawToTexture(mTextureId, mMatrix);
mFilter.drawFrame(id, mMatrix);
}
mDisplaySurface.swapBuffers();
}
/**
* 添加新的一幀
*/
public void addNewFrame() {
synchronized (mSyncFrameNum) {
++mFrameNum;
removeMessages(MSG_FRAME);
sendMessageAtFrontOfQueue(obtainMessage(MSG_FRAME));
}
}
接下來我們來看看CameraFilter 類,主要是用于繪制相機預(yù)覽幀的,實現(xiàn)如下:
public class CameraFilter extends BaseImageFilter {
private static final String VERTEX_SHADER =
"uniform mat4 uMVPMatrix; \n" +
"uniform mat4 uTexMatrix; \n" +
"attribute vec4 aPosition; \n" +
"attribute vec4 aTextureCoord; \n" +
"varying vec2 textureCoordinate; \n" +
"void main() { \n" +
" gl_Position = uMVPMatrix * aPosition; \n" +
" textureCoordinate = (uTexMatrix * aTextureCoord).xy; \n" +
"} \n";
private static final String FRAGMENT_SHADER_OES =
"#extension GL_OES_EGL_image_external : require \n" +
"precision mediump float; \n" +
"varying vec2 textureCoordinate; \n" +
"uniform samplerExternalOES sTexture; \n" +
"void main() { \n" +
" gl_FragColor = texture2D(sTexture, textureCoordinate); \n" +
"} \n";
private int[] mFramebuffers;
private int[] mFramebufferTextures;
private int mFrameWidth = -1;
private int mFrameHeight = -1;
public CameraFilter() {
mProgramHandle = GlUtil.createProgram(VERTEX_SHADER, FRAGMENT_SHADER_OES);
muMVPMatrixLoc = GLES30.glGetUniformLocation(mProgramHandle, "uMVPMatrix");
muTexMatrixLoc = GLES30.glGetUniformLocation(mProgramHandle, "uTexMatrix");
maPositionLoc = GLES30.glGetAttribLocation(mProgramHandle, "aPosition");
maTextureCoordLoc = GLES30.glGetAttribLocation(mProgramHandle, "aTextureCoord");
}
@Override
public int getTextureType() {
return GLES11Ext.GL_TEXTURE_EXTERNAL_OES;
}
@Override
public void drawFrame(int textureId, float[] texMatrix) {
GLES30.glUseProgram(mProgramHandle);
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
GLES30.glUniformMatrix4fv(muMVPMatrixLoc, 1, false, GlUtil.IDENTITY_MATRIX, 0);
GLES30.glUniformMatrix4fv(muTexMatrixLoc, 1, false, texMatrix, 0);
runPendingOnDrawTasks();
GLES30.glEnableVertexAttribArray(maPositionLoc);
GLES30.glVertexAttribPointer(maPositionLoc, mCoordsPerVertex,
GLES30.GL_FLOAT, false, mVertexStride, mVertexArray);
GLES30.glEnableVertexAttribArray(maTextureCoordLoc);
GLES30.glVertexAttribPointer(maTextureCoordLoc, 2,
GLES30.GL_FLOAT, false, mTexCoordStride, mTexCoordArray);
onDrawArraysBegin();
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, mVertexCount);
GLES30.glDisableVertexAttribArray(maPositionLoc);
GLES30.glDisableVertexAttribArray(maTextureCoordLoc);
onDrawArraysAfter();
GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
GLES30.glUseProgram(0);
}
/**
* 將SurfaceTexture的紋理繪制到FBO
* @param textureId
* @param texMatrix
* @return 繪制完成返回綁定到Framebuffer中的Texture
*/
public int drawToTexture(int textureId, float[] texMatrix) {
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFramebuffers[0]);
GLES30.glUseProgram(mProgramHandle);
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
GLES30.glUniformMatrix4fv(muMVPMatrixLoc, 1, false, GlUtil.IDENTITY_MATRIX, 0);
GLES30.glUniformMatrix4fv(muTexMatrixLoc, 1, false, texMatrix, 0);
GLES30.glEnableVertexAttribArray(maPositionLoc);
GLES30.glVertexAttribPointer(maPositionLoc, mCoordsPerVertex,
GLES30.GL_FLOAT, false, mVertexStride, mVertexArray);
GLES30.glEnableVertexAttribArray(maTextureCoordLoc);
GLES30.glVertexAttribPointer(maTextureCoordLoc, 2,
GLES30.GL_FLOAT, false, mTexCoordStride, getTexCoordArray());
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, mVertexCount);
GLES30.glFinish();
GLES30.glDisableVertexAttribArray(maPositionLoc);
GLES30.glDisableVertexAttribArray(maTextureCoordLoc);
GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
GLES30.glUseProgram(0);
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);
return mFramebufferTextures[0];
}
public void initCameraFramebuffer(int width, int height) {
if (mFramebuffers != null && (mFrameWidth != width || mFrameHeight != height)) {
destroyFramebuffer();
}
if (mFramebuffers == null) {
mFrameWidth = width;
mFrameHeight = height;
mFramebuffers = new int[1];
mFramebufferTextures = new int[1];
GlUtil.createSampler2DFrameBuff(mFramebuffers, mFramebufferTextures, width, height);
}
}
public void destroyFramebuffer() {
if (mFramebufferTextures != null) {
GLES30.glDeleteTextures(1, mFramebufferTextures, 0);
mFramebufferTextures = null;
}
if (mFramebuffers != null) {
GLES30.glDeleteFramebuffers(1, mFramebuffers, 0);
mFramebuffers = null;
}
mImageWidth = -1;
mImageHeight = -1;
}
}
CameraFilter 是繼承于BaseImageFilter類的,里面的成員變量也是來源于BaseImageFilter類。我們來看看BaseImageFilter類是如何實現(xiàn)的:
public class BaseImageFilter {
protected static final String VERTEX_SHADER =
"uniform mat4 uMVPMatrix; \n" +
"uniform mat4 uTexMatrix; \n" +
"attribute vec4 aPosition; \n" +
"attribute vec4 aTextureCoord; \n" +
"varying vec2 textureCoordinate; \n" +
"void main() { \n" +
" gl_Position = uMVPMatrix * aPosition; \n" +
" textureCoordinate = (uTexMatrix * aTextureCoord).xy; \n" +
"} \n";
private static final String FRAGMENT_SHADER_2D =
"precision mediump float; \n" +
"varying vec2 textureCoordinate; \n" +
"uniform sampler2D sTexture; \n" +
"void main() { \n" +
" gl_FragColor = texture2D(sTexture, textureCoordinate); \n" +
"} \n";
protected static final int SIZEOF_FLOAT = 4;
protected static final float SquareVertices[] = {
-1.0f, -1.0f, // 0 bottom left
1.0f, -1.0f, // 1 bottom right
-1.0f, 1.0f, // 2 top left
1.0f, 1.0f, // 3 top right
};
protected static final float TextureVertices[] = {
0.0f, 0.0f, // 0 bottom left
1.0f, 0.0f, // 1 bottom right
0.0f, 1.0f, // 2 top left
1.0f, 1.0f // 3 top right
};
protected static final float TextureVertices_90[] = {
1.0f, 0.0f,
1.0f, 1.0f,
0.0f, 0.0f,
0.0f, 1.0f
};
protected static final float TextureVertices_180[] = {
1.0f, 1.0f,
0.0f, 1.0f,
1.0f, 0.0f,
0.0f, 0.0f
};
protected static final float TextureVertices_270[] = {
0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 1.0f,
0.0f, 0.0f
};
private static final FloatBuffer FULL_RECTANGLE_BUF =
GlUtil.createFloatBuffer(SquareVertices);
protected FloatBuffer mVertexArray = FULL_RECTANGLE_BUF;
protected FloatBuffer mTexCoordArray = GlUtil.createFloatBuffer(TextureVertices);
protected int mCoordsPerVertex = 2;
protected int mVertexStride = mCoordsPerVertex * SIZEOF_FLOAT;
protected int mVertexCount = SquareVertices.length / mCoordsPerVertex;
protected int mTexCoordStride = 2 * SIZEOF_FLOAT;
protected int mProgramHandle;
protected int muMVPMatrixLoc;
protected int muTexMatrixLoc;
protected int maPositionLoc;
protected int maTextureCoordLoc;
// 渲染的Image的寬高
protected int mImageWidth;
protected int mImageHeight;
// 顯示輸出的寬高
protected int mDisplayWidth;
protected int mDisplayHeight;
private final LinkedList<Runnable> mRunOnDraw;
public BaseImageFilter() {
this(VERTEX_SHADER, FRAGMENT_SHADER_2D);
}
public BaseImageFilter(String vertexShader, String fragmentShader) {
mRunOnDraw = new LinkedList<>();
mProgramHandle = GlUtil.createProgram(vertexShader, fragmentShader);
maPositionLoc = GLES30.glGetAttribLocation(mProgramHandle, "aPosition");
maTextureCoordLoc = GLES30.glGetAttribLocation(mProgramHandle, "aTextureCoord");
muMVPMatrixLoc = GLES30.glGetUniformLocation(mProgramHandle, "uMVPMatrix");
muTexMatrixLoc = GLES30.glGetUniformLocation(mProgramHandle, "uTexMatrix");
}
/**
* Surface發(fā)生變化時調(diào)用
* @param width
* @param height
*/
public void onInputSizeChanged(int width, int height) {
mImageWidth = width;
mImageHeight = height;
}
/**
* 顯示視圖發(fā)生變化時調(diào)用
* @param width
* @param height
*/
public void onDisplayChanged(int width, int height) {
mDisplayWidth = width;
mDisplayHeight = height;
}
/**
* 繪制到FBO
* @param textureId
* @param texMatrix
*/
public void drawFrame(int textureId, float[] texMatrix) {
GLES30.glUseProgram(mProgramHandle);
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId);
GLES30.glUniformMatrix4fv(muMVPMatrixLoc, 1, false, GlUtil.IDENTITY_MATRIX, 0);
GLES30.glUniformMatrix4fv(muTexMatrixLoc, 1, false, texMatrix, 0);
runPendingOnDrawTasks();
GLES30.glEnableVertexAttribArray(maPositionLoc);
GLES30.glVertexAttribPointer(maPositionLoc, mCoordsPerVertex,
GLES30.GL_FLOAT, false, mVertexStride, mVertexArray);
GLES30.glEnableVertexAttribArray(maTextureCoordLoc);
GLES30.glVertexAttribPointer(maTextureCoordLoc, 2,
GLES30.GL_FLOAT, false, mTexCoordStride, mTexCoordArray);
onDrawArraysBegin();
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, mVertexCount);
GLES30.glDisableVertexAttribArray(maPositionLoc);
GLES30.glDisableVertexAttribArray(maTextureCoordLoc);
onDrawArraysAfter();
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);
GLES30.glUseProgram(0);
}
/**
* 獲取Texture類型
* GLES30.TEXTURE_2D / GLES11Ext.GL_TEXTURE_EXTERNAL_OES等
*/
public int getTextureType() {
return GLES30.GL_TEXTURE_2D;
}
/**
* 調(diào)用drawArrays之前,方便添加其他屬性
*/
public void onDrawArraysBegin() {
}
/**
* drawArrays調(diào)用之后,方便銷毀其他屬性
*/
public void onDrawArraysAfter() {
}
/**
* 釋放資源
*/
public void release() {
GLES30.glDeleteProgram(mProgramHandle);
mProgramHandle = -1;
}
public FloatBuffer getTexCoordArray() {
FloatBuffer result = null;
switch (CameraUtils.getPreviewOrientation()) {
case 0:
result = GlUtil.createFloatBuffer(TextureVertices);
break;
case 90:
result = GlUtil.createFloatBuffer(TextureVertices_90);
break;
case 180:
result = GlUtil.createFloatBuffer(TextureVertices_180);
break;
case 270:
result = GlUtil.createFloatBuffer(TextureVertices_270);
break;
default:
result = GlUtil.createFloatBuffer(TextureVertices);
}
return result;
}
///------------------ 同一變量(uniform)設(shè)置 ------------------------///
protected void setInteger(final int location, final int intValue) {
runOnDraw(new Runnable() {
@Override
public void run() {
GLES30.glUniform1i(location, intValue);
}
});
}
protected void setFloat(final int location, final float floatValue) {
runOnDraw(new Runnable() {
@Override
public void run() {
GLES30.glUniform1f(location, floatValue);
}
});
}
protected void setFloatVec2(final int location, final float[] arrayValue) {
runOnDraw(new Runnable() {
@Override
public void run() {
GLES30.glUniform2fv(location, 1, FloatBuffer.wrap(arrayValue));
}
});
}
protected void setFloatVec3(final int location, final float[] arrayValue) {
runOnDraw(new Runnable() {
@Override
public void run() {
GLES30.glUniform3fv(location, 1, FloatBuffer.wrap(arrayValue));
}
});
}
protected void setFloatVec4(final int location, final float[] arrayValue) {
runOnDraw(new Runnable() {
@Override
public void run() {
GLES30.glUniform4fv(location, 1, FloatBuffer.wrap(arrayValue));
}
});
}
protected void setFloatArray(final int location, final float[] arrayValue) {
runOnDraw(new Runnable() {
@Override
public void run() {
GLES30.glUniform1fv(location, arrayValue.length, FloatBuffer.wrap(arrayValue));
}
});
}
protected void setPoint(final int location, final PointF point) {
runOnDraw(new Runnable() {
@Override
public void run() {
float[] vec2 = new float[2];
vec2[0] = point.x;
vec2[1] = point.y;
GLES30.glUniform2fv(location, 1, vec2, 0);
}
});
}
protected void setUniformMatrix3f(final int location, final float[] matrix) {
runOnDraw(new Runnable() {
@Override
public void run() {
GLES30.glUniformMatrix3fv(location, 1, false, matrix, 0);
}
});
}
protected void setUniformMatrix4f(final int location, final float[] matrix) {
runOnDraw(new Runnable() {
@Override
public void run() {
GLES30.glUniformMatrix4fv(location, 1, false, matrix, 0);
}
});
}
protected void runOnDraw(final Runnable runnable) {
synchronized (mRunOnDraw) {
mRunOnDraw.addLast(runnable);
}
}
protected void runPendingOnDrawTasks() {
while (!mRunOnDraw.isEmpty()) {
mRunOnDraw.removeFirst().run();
}
}
}
至此,使用SurfaceView + OpenGLES 繪制Camera預(yù)覽數(shù)據(jù)的整體框架搭建完成。接下來可以在此基礎(chǔ)上添加各種濾鏡,添加錄制等功能了。詳細實現(xiàn)過程可以參考本人開發(fā)的相機項目CainCamera,該項目地址如下:
https://github.com/CainKernel/CainCamera
備注:
該項目主要是用來熟悉Camera 開發(fā)流程的,我會不定期修復(fù)開發(fā)過程中遇到的Bug,并逐步加入新的濾鏡,新濾鏡的加入同時也會新增文章介紹濾鏡使用到的相關(guān)數(shù)學(xué)知識。