diff options
Diffstat (limited to 'src/com/android/camera/MosaicPreviewRenderer.java')
-rw-r--r-- | src/com/android/camera/MosaicPreviewRenderer.java | 264 |
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]; + } +} |