summaryrefslogtreecommitdiffstats
path: root/src/com/android/camera/MosaicPreviewRenderer.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/camera/MosaicPreviewRenderer.java')
-rw-r--r--src/com/android/camera/MosaicPreviewRenderer.java264
1 files changed, 264 insertions, 0 deletions
diff --git a/src/com/android/camera/MosaicPreviewRenderer.java b/src/com/android/camera/MosaicPreviewRenderer.java
new file mode 100644
index 000000000..e12fe432e
--- /dev/null
+++ b/src/com/android/camera/MosaicPreviewRenderer.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camera;
+
+import android.annotation.TargetApi;
+import android.graphics.SurfaceTexture;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import com.android.gallery3d.common.ApiHelper;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+import javax.microedition.khronos.opengles.GL10;
+
+@TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB) // uses SurfaceTexture
+public class MosaicPreviewRenderer {
+ private static final String TAG = "MosaicPreviewRenderer";
+ private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+ private static final boolean DEBUG = false;
+
+ private int mWidth; // width of the view in UI
+ private int mHeight; // height of the view in UI
+
+ private boolean mIsLandscape = true;
+ private final float[] mTransformMatrix = new float[16];
+
+ private ConditionVariable mEglThreadBlockVar = new ConditionVariable();
+ private HandlerThread mEglThread;
+ private EGLHandler mEglHandler;
+
+ private EGLConfig mEglConfig;
+ private EGLDisplay mEglDisplay;
+ private EGLContext mEglContext;
+ private EGLSurface mEglSurface;
+ private SurfaceTexture mMosaicOutputSurfaceTexture;
+ private SurfaceTexture mInputSurfaceTexture;
+ private EGL10 mEgl;
+ private GL10 mGl;
+
+ private class EGLHandler extends Handler {
+ public static final int MSG_INIT_EGL_SYNC = 0;
+ public static final int MSG_SHOW_PREVIEW_FRAME_SYNC = 1;
+ public static final int MSG_SHOW_PREVIEW_FRAME = 2;
+ public static final int MSG_ALIGN_FRAME_SYNC = 3;
+ public static final int MSG_RELEASE = 4;
+
+ public EGLHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_INIT_EGL_SYNC:
+ doInitGL();
+ mEglThreadBlockVar.open();
+ break;
+ case MSG_SHOW_PREVIEW_FRAME_SYNC:
+ doShowPreviewFrame();
+ mEglThreadBlockVar.open();
+ break;
+ case MSG_SHOW_PREVIEW_FRAME:
+ doShowPreviewFrame();
+ break;
+ case MSG_ALIGN_FRAME_SYNC:
+ doAlignFrame();
+ mEglThreadBlockVar.open();
+ break;
+ case MSG_RELEASE:
+ doRelease();
+ break;
+ }
+ }
+
+ private void doAlignFrame() {
+ mInputSurfaceTexture.updateTexImage();
+ mInputSurfaceTexture.getTransformMatrix(mTransformMatrix);
+
+ MosaicRenderer.setWarping(true);
+ // Call preprocess to render it to low-res and high-res RGB textures.
+ MosaicRenderer.preprocess(mTransformMatrix);
+ // Now, transfer the textures from GPU to CPU memory for processing
+ MosaicRenderer.transferGPUtoCPU();
+ MosaicRenderer.updateMatrix();
+ draw();
+ mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
+ }
+
+ private void doShowPreviewFrame() {
+ mInputSurfaceTexture.updateTexImage();
+ mInputSurfaceTexture.getTransformMatrix(mTransformMatrix);
+
+ MosaicRenderer.setWarping(false);
+ // Call preprocess to render it to low-res and high-res RGB textures.
+ MosaicRenderer.preprocess(mTransformMatrix);
+ MosaicRenderer.updateMatrix();
+ draw();
+ mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
+ }
+
+ private void doInitGL() {
+ // These are copied from GLSurfaceView
+ mEgl = (EGL10) EGLContext.getEGL();
+ mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+ if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
+ throw new RuntimeException("eglGetDisplay failed");
+ }
+ int[] version = new int[2];
+ if (!mEgl.eglInitialize(mEglDisplay, version)) {
+ throw new RuntimeException("eglInitialize failed");
+ } else {
+ Log.v(TAG, "EGL version: " + version[0] + '.' + version[1]);
+ }
+ int[] attribList = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
+ mEglConfig = chooseConfig(mEgl, mEglDisplay);
+ mEglContext = mEgl.eglCreateContext(mEglDisplay, mEglConfig, EGL10.EGL_NO_CONTEXT,
+ attribList);
+
+ if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
+ throw new RuntimeException("failed to createContext");
+ }
+ mEglSurface = mEgl.eglCreateWindowSurface(
+ mEglDisplay, mEglConfig, mMosaicOutputSurfaceTexture, null);
+ if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
+ throw new RuntimeException("failed to createWindowSurface");
+ }
+
+ if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ throw new RuntimeException("failed to eglMakeCurrent");
+ }
+
+ mGl = (GL10) mEglContext.getGL();
+
+ mInputSurfaceTexture = new SurfaceTexture(MosaicRenderer.init());
+ MosaicRenderer.reset(mWidth, mHeight, mIsLandscape);
+ }
+
+ private void doRelease() {
+ mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
+ mEgl.eglDestroyContext(mEglDisplay, mEglContext);
+ mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_CONTEXT);
+ mEgl.eglTerminate(mEglDisplay);
+ mEglSurface = null;
+ mEglContext = null;
+ mEglDisplay = null;
+ releaseSurfaceTexture(mInputSurfaceTexture);
+ mEglThread.quit();
+ }
+
+ @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
+ private void releaseSurfaceTexture(SurfaceTexture st) {
+ if (ApiHelper.HAS_RELEASE_SURFACE_TEXTURE) {
+ st.release();
+ }
+ }
+
+ // Should be called from other thread.
+ public void sendMessageSync(int msg) {
+ mEglThreadBlockVar.close();
+ sendEmptyMessage(msg);
+ mEglThreadBlockVar.block();
+ }
+
+ }
+
+ public MosaicPreviewRenderer(SurfaceTexture tex, int w, int h, boolean isLandscape) {
+ mMosaicOutputSurfaceTexture = tex;
+ mWidth = w;
+ mHeight = h;
+ mIsLandscape = isLandscape;
+
+ mEglThread = new HandlerThread("PanoramaRealtimeRenderer");
+ mEglThread.start();
+ mEglHandler = new EGLHandler(mEglThread.getLooper());
+
+ // We need to sync this because the generation of surface texture for input is
+ // done here and the client will continue with the assumption that the
+ // generation is completed.
+ mEglHandler.sendMessageSync(EGLHandler.MSG_INIT_EGL_SYNC);
+ }
+
+ public void release() {
+ mEglHandler.sendEmptyMessage(EGLHandler.MSG_RELEASE);
+ }
+
+ public void showPreviewFrameSync() {
+ mEglHandler.sendMessageSync(EGLHandler.MSG_SHOW_PREVIEW_FRAME_SYNC);
+ }
+
+ public void showPreviewFrame() {
+ mEglHandler.sendEmptyMessage(EGLHandler.MSG_SHOW_PREVIEW_FRAME);
+ }
+
+ public void alignFrameSync() {
+ mEglHandler.sendMessageSync(EGLHandler.MSG_ALIGN_FRAME_SYNC);
+ }
+
+ public SurfaceTexture getInputSurfaceTexture() {
+ return mInputSurfaceTexture;
+ }
+
+ private void draw() {
+ MosaicRenderer.step();
+ }
+
+ private static void checkEglError(String prompt, EGL10 egl) {
+ int error;
+ while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) {
+ Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error));
+ }
+ }
+
+ private static final int EGL_OPENGL_ES2_BIT = 4;
+ private static final int[] CONFIG_SPEC = new int[] {
+ EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL10.EGL_RED_SIZE, 8,
+ EGL10.EGL_GREEN_SIZE, 8,
+ EGL10.EGL_BLUE_SIZE, 8,
+ EGL10.EGL_NONE
+ };
+
+ private static EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+ int[] numConfig = new int[1];
+ if (!egl.eglChooseConfig(display, CONFIG_SPEC, null, 0, numConfig)) {
+ throw new IllegalArgumentException("eglChooseConfig failed");
+ }
+
+ int numConfigs = numConfig[0];
+ if (numConfigs <= 0) {
+ throw new IllegalArgumentException("No configs match configSpec");
+ }
+
+ EGLConfig[] configs = new EGLConfig[numConfigs];
+ if (!egl.eglChooseConfig(
+ display, CONFIG_SPEC, configs, numConfigs, numConfig)) {
+ throw new IllegalArgumentException("eglChooseConfig#2 failed");
+ }
+
+ return configs[0];
+ }
+}