From b59bb8800bf349c5e407997e10180cc1e6e417ab Mon Sep 17 00:00:00 2001 From: Mattia Iavarone Date: Sun, 4 Mar 2018 21:45:42 +0100 Subject: [PATCH] Add GLCameraPreview based on GLSurfaceView, preview appears to be working with some bugs --- ...reviewTest.java => CameraPreviewTest.java} | 2 +- .../cameraview/CameraViewTest.java | 24 +++ .../cameraview/GLCameraPreviewTest.java | 19 ++ .../cameraview/SurfaceCameraPreviewTest.java | 2 +- .../cameraview/TextureCameraPreviewTest.java | 2 +- .../otaliastudios/cameraview/CameraView.java | 6 + .../main/res/layout/cameraview_gl_view.xml | 14 ++ cameraview/src/main/res/values/attrs.xml | 2 + .../cameraview/CameraPreview.java | 6 + .../cameraview/GLCameraPreview.java | 134 +++++++++++++ .../otaliastudios/cameraview/GLElement.java | 92 +++++++++ .../otaliastudios/cameraview/GLViewport.java | 179 ++++++++++++++++++ demo/src/main/res/layout/activity_camera.xml | 5 +- 13 files changed, 482 insertions(+), 5 deletions(-) rename cameraview/src/androidTest/java/com/otaliastudios/cameraview/{PreviewTest.java => CameraPreviewTest.java} (98%) create mode 100644 cameraview/src/androidTest/java/com/otaliastudios/cameraview/GLCameraPreviewTest.java create mode 100644 cameraview/src/main/res/layout/cameraview_gl_view.xml create mode 100644 cameraview/src/main/views/com/otaliastudios/cameraview/GLCameraPreview.java create mode 100644 cameraview/src/main/views/com/otaliastudios/cameraview/GLElement.java create mode 100644 cameraview/src/main/views/com/otaliastudios/cameraview/GLViewport.java diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/PreviewTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraPreviewTest.java similarity index 98% rename from cameraview/src/androidTest/java/com/otaliastudios/cameraview/PreviewTest.java rename to cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraPreviewTest.java index eb1fbf88..3d35c998 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/PreviewTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraPreviewTest.java @@ -17,7 +17,7 @@ import static org.junit.Assert.*; import static org.mockito.Mockito.*; -public abstract class PreviewTest extends BaseTest { +public abstract class CameraPreviewTest extends BaseTest { protected abstract CameraPreview createPreview(Context context, ViewGroup parent, CameraPreview.SurfaceCallback callback); diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java index 08ee413e..d1839109 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java @@ -68,6 +68,30 @@ public class CameraViewTest extends BaseTest { hasPermissions = false; } + //region testLifecycle + + @Test + public void testStart() { + cameraView.start(); + verify(mockPreview, times(1)).onResume(); + // Can't verify controller, depends on permissions. + // See to-do at the end. + } + + @Test + public void testStop() { + cameraView.stop(); + verify(mockPreview, times(1)).onPause(); + verify(mockController, times(1)).stop(); + } + + @Test + public void testDestroy() { + cameraView.destroy(); + verify(mockPreview, times(1)).onDestroy(); + verify(mockController, times(1)).destroy(); + } + //region testDefaults @Test diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/GLCameraPreviewTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/GLCameraPreviewTest.java new file mode 100644 index 00000000..e766f856 --- /dev/null +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/GLCameraPreviewTest.java @@ -0,0 +1,19 @@ +package com.otaliastudios.cameraview; + + +import android.content.Context; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.view.ViewGroup; + +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class GLCameraPreviewTest extends CameraPreviewTest { + + @Override + protected CameraPreview createPreview(Context context, ViewGroup parent, CameraPreview.SurfaceCallback callback) { + return new GLCameraPreview(context, parent, callback); + } +} diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/SurfaceCameraPreviewTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/SurfaceCameraPreviewTest.java index d3d0cdb6..14a7e849 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/SurfaceCameraPreviewTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/SurfaceCameraPreviewTest.java @@ -10,7 +10,7 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @SmallTest -public class SurfaceCameraPreviewTest extends PreviewTest { +public class SurfaceCameraPreviewTest extends CameraPreviewTest { @Override protected CameraPreview createPreview(Context context, ViewGroup parent, CameraPreview.SurfaceCallback callback) { diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/TextureCameraPreviewTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/TextureCameraPreviewTest.java index 8a1f056e..994aacce 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/TextureCameraPreviewTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/TextureCameraPreviewTest.java @@ -10,7 +10,7 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @SmallTest -public class TextureCameraPreviewTest extends PreviewTest { +public class TextureCameraPreviewTest extends CameraPreviewTest { @Override protected CameraPreview createPreview(Context context, ViewGroup parent, CameraPreview.SurfaceCallback callback) { diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java index 656ae697..7f0fddd1 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java @@ -70,6 +70,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { TapGestureLayout mTapGestureLayout; ScrollGestureLayout mScrollGestureLayout; private boolean mKeepScreenOn; + private boolean mExperimental; // Threading private Handler mUiHandler; @@ -94,6 +95,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { // Self managed boolean playSounds = a.getBoolean(R.styleable.CameraView_cameraPlaySounds, DEFAULT_PLAY_SOUNDS); + mExperimental = a.getBoolean(R.styleable.CameraView_cameraExperimental, false); // Camera controller params Facing facing = Facing.fromValue(a.getInteger(R.styleable.CameraView_cameraFacing, Facing.DEFAULT.value())); @@ -229,6 +231,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { protected CameraPreview instantiatePreview(Context context, ViewGroup container) { // TextureView is not supported without hardware acceleration. LOG.w("preview:", "isHardwareAccelerated:", isHardwareAccelerated()); + if (mExperimental) return new GLCameraPreview(context, container, null); return isHardwareAccelerated() ? new TextureCameraPreview(context, container, null) : new SurfaceCameraPreview(context, container, null); @@ -580,6 +583,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) public void start() { if (!isEnabled()) return; + if (mCameraPreview != null) mCameraPreview.onResume(); if (checkPermissions(getMode(), getAudio())) { // Update display orientation for current CameraController @@ -650,6 +654,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) public void stop() { mCameraController.stop(); + if (mCameraPreview != null) mCameraPreview.onPause(); } @@ -662,6 +667,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { clearCameraListeners(); clearFrameProcessors(); mCameraController.destroy(); + if (mCameraPreview != null) mCameraPreview.onDestroy(); } //endregion diff --git a/cameraview/src/main/res/layout/cameraview_gl_view.xml b/cameraview/src/main/res/layout/cameraview_gl_view.xml new file mode 100644 index 00000000..0525ee6e --- /dev/null +++ b/cameraview/src/main/res/layout/cameraview_gl_view.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/cameraview/src/main/res/values/attrs.xml b/cameraview/src/main/res/values/attrs.xml index 5c217374..d5c0e5b7 100644 --- a/cameraview/src/main/res/values/attrs.xml +++ b/cameraview/src/main/res/values/attrs.xml @@ -118,5 +118,7 @@ + + \ No newline at end of file diff --git a/cameraview/src/main/views/com/otaliastudios/cameraview/CameraPreview.java b/cameraview/src/main/views/com/otaliastudios/cameraview/CameraPreview.java index b7210575..2fcaf630 100644 --- a/cameraview/src/main/views/com/otaliastudios/cameraview/CameraPreview.java +++ b/cameraview/src/main/views/com/otaliastudios/cameraview/CameraPreview.java @@ -102,6 +102,12 @@ abstract class CameraPreview { mSurfaceHeight = 0; } + void onResume() {} + + void onPause() {} + + void onDestroy() {} + final boolean isReady() { return mSurfaceWidth > 0 && mSurfaceHeight > 0; } diff --git a/cameraview/src/main/views/com/otaliastudios/cameraview/GLCameraPreview.java b/cameraview/src/main/views/com/otaliastudios/cameraview/GLCameraPreview.java new file mode 100644 index 00000000..30782dab --- /dev/null +++ b/cameraview/src/main/views/com/otaliastudios/cameraview/GLCameraPreview.java @@ -0,0 +1,134 @@ +package com.otaliastudios.cameraview; + +import android.content.Context; +import android.graphics.SurfaceTexture; +import android.opengl.GLSurfaceView; +import android.support.annotation.NonNull; +import android.view.LayoutInflater; +import android.view.SurfaceHolder; +import android.view.View; +import android.view.ViewGroup; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +class GLCameraPreview extends CameraPreview implements GLSurfaceView.Renderer { + + private boolean mDispatched; + private final float[] mTransformMatrix = new float[16]; + private int mTextureId = -1; + private SurfaceTexture mSurfaceTexture; + private GLViewport mViewport; + + GLCameraPreview(Context context, ViewGroup parent, SurfaceCallback callback) { + super(context, parent, callback); + } + + @NonNull + @Override + protected GLSurfaceView onCreateView(Context context, ViewGroup parent) { + View root = LayoutInflater.from(context).inflate(R.layout.cameraview_gl_view, parent, false); + parent.addView(root, 0); + GLSurfaceView glView = root.findViewById(R.id.gl_surface_view); + glView.setEGLContextClientVersion(2); + glView.setRenderer(this); + glView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + glView.getHolder().addCallback(new SurfaceHolder.Callback() { + public void surfaceCreated(SurfaceHolder holder) {} + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + onSurfaceDestroyed(); + } + }); + return glView; + } + + @Override + void onResume() { + super.onResume(); + getView().onResume(); + } + + @Override + void onPause() { + super.onPause(); + getView().onPause(); + } + + @Override + void onDestroy() { + super.onDestroy(); + if (mSurfaceTexture != null) { + mSurfaceTexture.release(); + mSurfaceTexture = null; + } + if (mViewport != null) { + mViewport.release(); + mViewport = null; + } + } + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + mViewport = new GLViewport(); + mTextureId = mViewport.createTexture(); + mSurfaceTexture = new SurfaceTexture(mTextureId); + mSurfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() { + @Override + public void onFrameAvailable(SurfaceTexture surfaceTexture) { + // requestRender is thread-safe. + getView().requestRender(); + } + }); + } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + if (!mDispatched) { + onSurfaceAvailable(width, height); + mDispatched = true; + } else { + onSurfaceSizeChanged(width, height); + } + } + + + @Override + public void onDrawFrame(GL10 gl) { + // Latch the latest frame. If there isn't anything new, + // we'll just re-use whatever was there before. + mSurfaceTexture.updateTexImage(); + if (mDesiredWidth <= 0 || mDesiredHeight <= 0) { + // Skip drawing. Camera was not opened. + return; + } + + // Draw the video frame. + mSurfaceTexture.getTransformMatrix(mTransformMatrix); + mViewport.drawFrame(mTextureId, mTransformMatrix); + } + + @Override + Class getOutputClass() { + return SurfaceTexture.class; + } + + @Override + SurfaceTexture getOutput() { + return mSurfaceTexture; + } + + + @Override + boolean supportsCropping() { + return true; + } + + @Override + protected void crop() { + mCropTask.start(); + mCropTask.end(null); + } +} diff --git a/cameraview/src/main/views/com/otaliastudios/cameraview/GLElement.java b/cameraview/src/main/views/com/otaliastudios/cameraview/GLElement.java new file mode 100644 index 00000000..4c05cd29 --- /dev/null +++ b/cameraview/src/main/views/com/otaliastudios/cameraview/GLElement.java @@ -0,0 +1,92 @@ +package com.otaliastudios.cameraview; + + +import android.opengl.GLES11Ext; +import android.opengl.GLES20; +import android.opengl.Matrix; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +class GLElement { + + private final static CameraLogger LOG = CameraLogger.create(GLElement.class.getSimpleName()); + + // Identity matrix for general use. + protected static final float[] IDENTITY_MATRIX = new float[16]; + static { + Matrix.setIdentityM(IDENTITY_MATRIX, 0); + } + + // Check for GLES errors. + protected static void check(String opName) { + int error = GLES20.glGetError(); + if (error != GLES20.GL_NO_ERROR) { + LOG.e("Error during", opName, "glError 0x", Integer.toHexString(error)); + throw new RuntimeException(CameraLogger.lastMessage); + } + } + + // Check for valid location. + protected static void checkLocation(int location, String label) { + if (location < 0) { + LOG.e("Unable to locate", label, "in program"); + throw new RuntimeException(CameraLogger.lastMessage); + } + } + + // Compiles the given shader, returns a handle. + protected static int loadShader(int shaderType, String source) { + int shader = GLES20.glCreateShader(shaderType); + check("glCreateShader type=" + shaderType); + GLES20.glShaderSource(shader, source); + GLES20.glCompileShader(shader); + int[] compiled = new int[1]; + GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); + if (compiled[0] == 0) { + LOG.e("Could not compile shader", shaderType, ":", GLES20.glGetShaderInfoLog(shader)); + GLES20.glDeleteShader(shader); + shader = 0; + } + return shader; + } + + // Creates a program with given vertex shader and pixel shader. + protected static int createProgram(String vertexSource, String fragmentSource) { + int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); + if (vertexShader == 0) return 0; + int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); + if (pixelShader == 0) return 0; + + int program = GLES20.glCreateProgram(); + check("glCreateProgram"); + if (program == 0) { + LOG.e("Could not create program"); + } + GLES20.glAttachShader(program, vertexShader); + check("glAttachShader"); + GLES20.glAttachShader(program, pixelShader); + check("glAttachShader"); + GLES20.glLinkProgram(program); + int[] linkStatus = new int[1]; + GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); + if (linkStatus[0] != GLES20.GL_TRUE) { + LOG.e("Could not link program:", GLES20.glGetProgramInfoLog(program)); + GLES20.glDeleteProgram(program); + program = 0; + } + return program; + } + + // Allocates a direct float buffer, and populates it with the float array data. + protected static FloatBuffer floatBuffer(float[] coords) { + // Allocate a direct ByteBuffer, using 4 bytes per float, and copy coords into it. + ByteBuffer bb = ByteBuffer.allocateDirect(coords.length * 4); + bb.order(ByteOrder.nativeOrder()); + FloatBuffer fb = bb.asFloatBuffer(); + fb.put(coords); + fb.position(0); + return fb; + } +} diff --git a/cameraview/src/main/views/com/otaliastudios/cameraview/GLViewport.java b/cameraview/src/main/views/com/otaliastudios/cameraview/GLViewport.java new file mode 100644 index 00000000..a8ced7a6 --- /dev/null +++ b/cameraview/src/main/views/com/otaliastudios/cameraview/GLViewport.java @@ -0,0 +1,179 @@ +package com.otaliastudios.cameraview; + + +import android.opengl.GLES11Ext; +import android.opengl.GLES20; +import android.opengl.Matrix; +import android.util.Log; + +import java.nio.FloatBuffer; + +class GLViewport extends GLElement { + + private final static CameraLogger LOG = CameraLogger.create(GLViewport.class.getSimpleName()); + + // Simple vertex shader. + private static final String SIMPLE_VERTEX_SHADER = + "uniform mat4 uMVPMatrix;\n" + + "uniform mat4 uTexMatrix;\n" + + "attribute vec4 aPosition;\n" + + "attribute vec4 aTextureCoord;\n" + + "varying vec2 vTextureCoord;\n" + + "void main() {\n" + + " gl_Position = uMVPMatrix * aPosition;\n" + + " vTextureCoord = (uTexMatrix * aTextureCoord).xy;\n" + + "}\n"; + + // Simple fragment shader for use with external 2D textures + // (e.g. what we get from SurfaceTexture). + private static final String SIMPLE_FRAGMENT_SHADER = + "#extension GL_OES_EGL_image_external : require\n" + + "precision mediump float;\n" + + "varying vec2 vTextureCoord;\n" + + "uniform samplerExternalOES sTexture;\n" + + "void main() {\n" + + " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + + "}\n"; + + // Stuff from Drawable2d.FULL_RECTANGLE + // A full square, extending from -1 to +1 in both dimensions. + // When the model/view/projection matrix is identity, this will exactly cover the viewport. + private static final float FULL_RECTANGLE_COORDS[] = { + -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 + }; + + // Stuff from Drawable2d.FULL_RECTANGLE + // A full square, extending from -1 to +1 in both dimensions. + private static final float FULL_RECTANGLE_TEX_COORDS[] = { + 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 + }; + + // Stuff from Drawable2d.FULL_RECTANGLE + private FloatBuffer mVertexArray = floatBuffer(FULL_RECTANGLE_COORDS); + private FloatBuffer mTexCoordArray = floatBuffer(FULL_RECTANGLE_TEX_COORDS); + private int mVertexCount = FULL_RECTANGLE_COORDS.length / 2; + private final int mCoordsPerVertex = 2; + private final int mVertexStride = 8; + private final int mTexCoordStride = 8; + + // Stuff from Texture2dProgram + private int mProgramHandle; + private int mTextureTarget; + private int muMVPMatrixLoc; + private int muTexMatrixLoc; + private int maPositionLoc; + private int maTextureCoordLoc; + + // private int muKernelLoc; // Used for filtering + // private int muTexOffsetLoc; // Used for filtering + // private int muColorAdjustLoc; // Used for filtering + + GLViewport() { + mTextureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES; + mProgramHandle = createProgram(SIMPLE_VERTEX_SHADER, SIMPLE_FRAGMENT_SHADER); + maPositionLoc = GLES20.glGetAttribLocation(mProgramHandle, "aPosition"); + checkLocation(maPositionLoc, "aPosition"); + maTextureCoordLoc = GLES20.glGetAttribLocation(mProgramHandle, "aTextureCoord"); + checkLocation(maTextureCoordLoc, "aTextureCoord"); + muMVPMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "uMVPMatrix"); + checkLocation(muMVPMatrixLoc, "uMVPMatrix"); + muTexMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "uTexMatrix"); + checkLocation(muTexMatrixLoc, "uTexMatrix"); + + // Stuff from Drawable2d.FULL_RECTANGLE + + } + + + void release() { + GLES20.glDeleteProgram(mProgramHandle); + mProgramHandle = -1; + } + + int createTexture() { + int[] textures = new int[1]; + GLES20.glGenTextures(1, textures, 0); + check("glGenTextures"); + + int texId = textures[0]; + GLES20.glBindTexture(mTextureTarget, texId); + check("glBindTexture " + texId); + + GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, + GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); + GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, + GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, + GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, + GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + check("glTexParameter"); + + return texId; + } + + void drawFrame(int textureId, float[] texMatrix) { + drawFrame(textureId, texMatrix, + IDENTITY_MATRIX, mVertexArray, 0, + mVertexCount, mCoordsPerVertex, + mVertexStride, mTexCoordArray, + mTexCoordStride); + } + + private void drawFrame(int textureId, float[] texMatrix, + float[] mvpMatrix, FloatBuffer vertexBuffer, int firstVertex, + int vertexCount, int coordsPerVertex, int vertexStride, + FloatBuffer texBuffer, int texStride) { + check("draw start"); + + // Select the program. + GLES20.glUseProgram(mProgramHandle); + check("glUseProgram"); + + // Set the texture. + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(mTextureTarget, textureId); + + // Copy the model / view / projection matrix over. + GLES20.glUniformMatrix4fv(muMVPMatrixLoc, 1, false, mvpMatrix, 0); + check("glUniformMatrix4fv"); + + // Copy the texture transformation matrix over. + GLES20.glUniformMatrix4fv(muTexMatrixLoc, 1, false, texMatrix, 0); + check("glUniformMatrix4fv"); + + // Enable the "aPosition" vertex attribute. + GLES20.glEnableVertexAttribArray(maPositionLoc); + check("glEnableVertexAttribArray"); + + // Connect vertexBuffer to "aPosition". + GLES20.glVertexAttribPointer(maPositionLoc, coordsPerVertex, + GLES20.GL_FLOAT, false, vertexStride, vertexBuffer); + check("glVertexAttribPointer"); + + // Enable the "aTextureCoord" vertex attribute. + GLES20.glEnableVertexAttribArray(maTextureCoordLoc); + check("glEnableVertexAttribArray"); + + // Connect texBuffer to "aTextureCoord". + GLES20.glVertexAttribPointer(maTextureCoordLoc, 2, + GLES20.GL_FLOAT, false, texStride, texBuffer); + check("glVertexAttribPointer"); + + // Draw the rect. + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, firstVertex, vertexCount); + check("glDrawArrays"); + + // Done -- disable vertex array, texture, and program. + GLES20.glDisableVertexAttribArray(maPositionLoc); + GLES20.glDisableVertexAttribArray(maTextureCoordLoc); + GLES20.glBindTexture(mTextureTarget, 0); + GLES20.glUseProgram(0); + } +} diff --git a/demo/src/main/res/layout/activity_camera.xml b/demo/src/main/res/layout/activity_camera.xml index 5398cbcf..a159747d 100644 --- a/demo/src/main/res/layout/activity_camera.xml +++ b/demo/src/main/res/layout/activity_camera.xml @@ -11,10 +11,11 @@