Last active
May 4, 2016 01:24
-
-
Save tonykwok/27e1c3daded1e89da9917676b74bd637 to your computer and use it in GitHub Desktop.
Don't leave the previous camera preview hanging on the screen, that looks weird if the aspect ratio or scene changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// We need to do this with OpenGL ES (*not* Canvas -- the "software render" bits | |
// are sticky). We can't stay connected to the Surface after we're done because | |
// that will prevent the camera from attaching. | |
// @param surface should be either Surface or SurfaceTexture | |
public static void clear(object surface) { | |
try { | |
if (Log.IS_DEBUG) Log.logDebug(TAG, "clear() using OpenGL ES: E"); | |
WindowSurface windowSurface = new WindowSurface(surface); | |
windowSurface.makeCurrent(); | |
GLES20.glClearColor(0, 0, 0, 0); | |
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); | |
windowSurface.swapBuffers(); | |
windowSurface.release(); | |
if (Log.IS_DEBUG) Log.logDebug(TAG, "clear() using OpenGL ES: X"); | |
} catch (RuntimeException exc) { | |
Log.e(TAG, "clear() using OpenGL ES failed.", exc); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.graphics.SurfaceTexture; | |
import android.opengl.EGL14; | |
import android.opengl.EGLConfig; | |
import android.opengl.EGLContext; | |
import android.opengl.EGLDisplay; | |
import android.opengl.EGLExt; | |
import android.opengl.EGLSurface; | |
import android.util.Log; | |
import android.view.Surface; | |
/** | |
* This file is based on the following AOSP sourcecode: | |
* cts/tests/tests/opengl/src/android/opengl/cts/FramebufferTest.java | |
*/ | |
public class WindowSurface { | |
private static final String TAG = WindowSurface.class.getSimpleName(); | |
private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY; | |
private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT; | |
private EGLSurface mEGLSurface = EGL14.EGL_NO_SURFACE; | |
private EGLConfig mEGLConfig = null; | |
private int mGlEsVersion = -1; | |
/** | |
* Associates an EGL surface with the native window surface. | |
* | |
* @param surface May be a Surface or SurfaceTexture. | |
*/ | |
public WindowSurface(Surface surface) { | |
this(null, surface, 2); | |
} | |
/** | |
* Associates an EGL surface with the native window surface. | |
* | |
* @param sharedContext The context to share, or null if sharing is not desired. | |
* @param surface May be a Surface or SurfaceTexture. | |
* @param glEsVersion The EGL_CONTEXT_CLIENT_VERSION to choose, must be 2 or 3. | |
*/ | |
public WindowSurface(EGLContext sharedContext, Object surface, int glEsVersion) { | |
// State check. | |
if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) { | |
throw new RuntimeException("EGL already set up"); | |
} | |
if (mEGLSurface != EGL14.EGL_NO_SURFACE) { | |
throw new RuntimeException("surface already created"); | |
} | |
// Arguments check. | |
if (sharedContext == null) { | |
sharedContext = EGL14.EGL_NO_CONTEXT; // context sharing is not desired. | |
} | |
if (!(surface instanceof Surface) && !(surface instanceof SurfaceTexture)) { | |
throw new RuntimeException("invalid surface: " + surface); | |
} | |
if (glEsVersion != 2 && glEsVersion != 3) { | |
throw new RuntimeException("invalid glEsVersion: " + glEsVersion); | |
} | |
// Prepares EGL display. | |
mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); | |
if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) { | |
throw new RuntimeException("unable to get EGL14 display"); | |
} | |
int[] version = new int[2]; | |
if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) { | |
mEGLDisplay = null; | |
throw new RuntimeException("unable to initialize EGL14"); | |
} | |
// Try to get a GLES3 context, if requested. | |
if (glEsVersion == 3) { | |
Log.logDebug(TAG, "Trying GLES 3"); | |
EGLConfig config = getConfig(3); | |
if (config != null) { | |
int[] attributes = { | |
EGL14.EGL_CONTEXT_CLIENT_VERSION, 3, | |
EGL14.EGL_NONE | |
}; | |
EGLContext context = EGL14.eglCreateContext(mEGLDisplay, | |
config, | |
sharedContext, | |
attributes, | |
0); | |
if (EGL14.eglGetError() == EGL14.EGL_SUCCESS) { | |
Log.logDebug(TAG, "Got GLES 3 config."); | |
mEGLConfig = config; | |
mEGLContext = context; | |
mGlEsVersion = 3; | |
} | |
} | |
} | |
// Fall back to GLES 2 if GLES 3 attempt failed. | |
if (mEGLContext == EGL14.EGL_NO_CONTEXT) { | |
EGLConfig config = getConfig(2); | |
if (config == null) { | |
throw new RuntimeException("Unable to find a suitable EGLConfig"); | |
} | |
int[] attributes = { | |
EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, | |
EGL14.EGL_NONE | |
}; | |
EGLContext context = EGL14.eglCreateContext( | |
mEGLDisplay, | |
config, | |
sharedContext, | |
attributes, | |
0); | |
checkEglError("eglCreateContext"); | |
mEGLConfig = config; | |
mEGLContext = context; | |
mGlEsVersion = 2; | |
} | |
// Confirm with query. | |
int[] values = new int[1]; | |
EGL14.eglQueryContext( | |
mEGLDisplay, | |
mEGLContext, | |
EGL14.EGL_CONTEXT_CLIENT_VERSION, | |
values, | |
0); | |
Log.logDebug(TAG, "EGLContext created, client version " + values[0]); | |
// Create a window surface, and attach it to the Surface we received. | |
int[] surfaceAttributes = { | |
EGL14.EGL_NONE | |
}; | |
EGLSurface eglSurface = EGL14.eglCreateWindowSurface( | |
mEGLDisplay, | |
mEGLConfig, | |
surface, | |
surfaceAttributes, | |
0); | |
checkEglError("eglCreateWindowSurface"); | |
if (eglSurface == null) { | |
throw new RuntimeException("surface was null"); | |
} | |
mEGLSurface = eglSurface; | |
} | |
/** | |
* Returns the GLES version this context is configured for (currently 2 or 3). | |
*/ | |
public int getGlEsVersion() { | |
return mGlEsVersion; | |
} | |
/** | |
* Makes our EGL context and surface current. | |
*/ | |
public void makeCurrent() { | |
if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) { | |
// called makeCurrent() before create? | |
Log.e(TAG, "makeCurrent w/o display"); | |
} | |
// Makes our EGL context current, using the supplied surface for both "draw" and "read". | |
if (!EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) { | |
throw new RuntimeException("eglMakeCurrent failed"); | |
} | |
} | |
/** | |
* Calls {@link EGL14#eglSwapBuffers}. Use this to "publish" the current frame. | |
* | |
* @return false on failure | |
*/ | |
public boolean swapBuffers() { | |
boolean result = EGL14.eglSwapBuffers(mEGLDisplay, mEGLSurface); | |
if (!result) { | |
Log.e(TAG, "swapBuffers() failed"); | |
} | |
return result; | |
} | |
/** | |
* Releases any resources associated with the EGL surface. | |
*/ | |
public void release() { | |
EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface); | |
mEGLSurface = EGL14.EGL_NO_SURFACE; | |
if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) { | |
// Android is unusual in that it uses a reference-counted EGLDisplay. | |
// So for every eglInitialize() we need an eglTerminate(). | |
EGL14.eglMakeCurrent( | |
mEGLDisplay, | |
EGL14.EGL_NO_SURFACE, | |
EGL14.EGL_NO_SURFACE, | |
EGL14.EGL_NO_CONTEXT); | |
EGL14.eglDestroyContext(mEGLDisplay, mEGLContext); | |
EGL14.eglReleaseThread(); | |
EGL14.eglTerminate(mEGLDisplay); | |
} | |
mEGLDisplay = EGL14.EGL_NO_DISPLAY; | |
mEGLContext = EGL14.EGL_NO_CONTEXT; | |
mEGLConfig = null; | |
} | |
/** | |
* Finds a suitable EGLConfig. | |
* | |
* @param glEsVersion Must be 2 or 3. | |
*/ | |
private EGLConfig getConfig(int glEsVersion) { | |
int renderableType = EGL14.EGL_OPENGL_ES2_BIT; | |
if (glEsVersion >= 3) { | |
renderableType |= EGLExt.EGL_OPENGL_ES3_BIT_KHR; | |
} | |
// The actual surface is generally RGBA or RGBX, so situationally omitting alpha | |
// doesn't really help. It can also lead to a huge performance hit on glReadPixels() | |
// when reading into a GL_RGBA buffer. | |
int[] attributes = { | |
EGL14.EGL_RED_SIZE, 8, | |
EGL14.EGL_GREEN_SIZE, 8, | |
EGL14.EGL_BLUE_SIZE, 8, | |
EGL14.EGL_ALPHA_SIZE, 8, | |
// EGL14.EGL_DEPTH_SIZE, 16, | |
// EGL14.EGL_STENCIL_SIZE, 8, | |
EGL14.EGL_RENDERABLE_TYPE, renderableType, | |
EGL14.EGL_NONE | |
}; | |
EGLConfig[] configs = new EGLConfig[1]; | |
int[] numConfigs = new int[1]; | |
if (!EGL14.eglChooseConfig( | |
mEGLDisplay, | |
attributes, | |
0, | |
configs, | |
0, | |
configs.length, | |
numConfigs, | |
0)) { | |
Log.e(TAG, "unable to find RGB8888 / " + glEsVersion + " EGLConfig"); | |
return null; | |
} | |
return configs[0]; | |
} | |
/** | |
* Checks for EGL errors. Throws an exception if an error has been raised. | |
*/ | |
private static void checkEglError(String msg) { | |
final int error = EGL14.eglGetError(); | |
if (error != EGL14.EGL_SUCCESS) { | |
throw new RuntimeException(msg + ": EGL error: 0x" + Integer.toHexString(error)); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment