MosaicPreviewRenderer.java revision 252da3c55a35c77fa4be4d51f79dac4aaf7cdb77
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.camera; 18 19import android.graphics.SurfaceTexture; 20import android.os.ConditionVariable; 21import android.os.Handler; 22import android.os.HandlerThread; 23import android.os.Looper; 24import android.os.Message; 25import android.util.Log; 26 27import javax.microedition.khronos.egl.EGL10; 28import javax.microedition.khronos.egl.EGLConfig; 29import javax.microedition.khronos.egl.EGLContext; 30import javax.microedition.khronos.egl.EGLDisplay; 31import javax.microedition.khronos.egl.EGLSurface; 32import javax.microedition.khronos.opengles.GL10; 33 34public class MosaicPreviewRenderer { 35 private static final String TAG = "MosaicPreviewRenderer"; 36 private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; 37 private static final boolean DEBUG = false; 38 39 private int mWidth; 40 private int mHeight; 41 private boolean mPaused; 42 43 private int mTextureId; 44 private boolean mIsLandscape = true; 45 private final float[] mTransformMatrix = new float[16]; 46 47 private ConditionVariable mEglThreadBlockVar = new ConditionVariable(); 48 private HandlerThread mEglThread; 49 private EGLHandler mEglHandler; 50 51 private EGLConfig mEglConfig; 52 private EGLDisplay mEglDisplay; 53 private EGLContext mEglContext; 54 private EGLSurface mEglSurface; 55 private SurfaceTexture mMosaicOutputSurfaceTexture; 56 private SurfaceTexture mInputSurfaceTexture; 57 private EGL10 mEgl; 58 private GL10 mGl; 59 60 private class EGLHandler extends Handler { 61 public static final int MSG_INIT_EGL_SYNC = 0; 62 public static final int MSG_SHOW_PREVIEW_FRAME_SYNC = 1; 63 public static final int MSG_SHOW_PREVIEW_FRAME = 2; 64 public static final int MSG_ALIGN_FRAME = 3; 65 public static final int MSG_RELEASE = 4; 66 67 public EGLHandler(Looper looper) { 68 super(looper); 69 } 70 71 @Override 72 public void handleMessage(Message msg) { 73 switch (msg.what) { 74 case MSG_INIT_EGL_SYNC: 75 doInitGL(); 76 mEglThreadBlockVar.open(); 77 break; 78 case MSG_SHOW_PREVIEW_FRAME_SYNC: 79 doShowPreviewFrame(); 80 mEglThreadBlockVar.open(); 81 break; 82 case MSG_SHOW_PREVIEW_FRAME: 83 doShowPreviewFrame(); 84 break; 85 case MSG_ALIGN_FRAME: 86 doAlignFrame(); 87 break; 88 case MSG_RELEASE: 89 doRelease(); 90 break; 91 } 92 } 93 94 private void doAlignFrame() { 95 mInputSurfaceTexture.updateTexImage(); 96 mInputSurfaceTexture.getTransformMatrix(mTransformMatrix); 97 98 MosaicRenderer.setWarping(true); 99 // Call preprocess to render it to low-res and high-res RGB textures. 100 MosaicRenderer.preprocess(mTransformMatrix); 101 // Now, transfer the textures from GPU to CPU memory for processing 102 MosaicRenderer.transferGPUtoCPU(); 103 MosaicRenderer.updateMatrix(); 104 draw(); 105 mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); 106 } 107 108 private void doShowPreviewFrame() { 109 mInputSurfaceTexture.updateTexImage(); 110 mInputSurfaceTexture.getTransformMatrix(mTransformMatrix); 111 112 MosaicRenderer.setWarping(false); 113 // Call preprocess to render it to low-res and high-res RGB textures. 114 MosaicRenderer.preprocess(mTransformMatrix); 115 MosaicRenderer.updateMatrix(); 116 draw(); 117 mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); 118 } 119 120 private void doInitGL() { 121 // These are copied from GLSurfaceView 122 mEgl = (EGL10) EGLContext.getEGL(); 123 mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); 124 if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { 125 throw new RuntimeException("eglGetDisplay failed"); 126 } 127 int[] version = new int[2]; 128 if (!mEgl.eglInitialize(mEglDisplay, version)) { 129 throw new RuntimeException("eglInitialize failed"); 130 } else { 131 Log.v(TAG, "EGL version: " + version[0] + '.' + version[1]); 132 } 133 int[] attribList = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; 134 mEglConfig = chooseConfig(mEgl, mEglDisplay); 135 mEglContext = mEgl.eglCreateContext(mEglDisplay, mEglConfig, EGL10.EGL_NO_CONTEXT, 136 attribList); 137 138 if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) { 139 throw new RuntimeException("failed to createContext"); 140 } 141 mEglSurface = mEgl.eglCreateWindowSurface( 142 mEglDisplay, mEglConfig, mMosaicOutputSurfaceTexture, null); 143 if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { 144 throw new RuntimeException("failed to createWindowSurface"); 145 } 146 147 if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 148 throw new RuntimeException("failed to eglMakeCurrent"); 149 } 150 151 mGl = (GL10) mEglContext.getGL(); 152 153 mInputSurfaceTexture = new SurfaceTexture(MosaicRenderer.init()); 154 MosaicRenderer.reset(mWidth, mHeight, mIsLandscape); 155 } 156 157 private void doRelease() { 158 mEgl.eglDestroySurface(mEglDisplay, mEglSurface); 159 mEgl.eglDestroyContext(mEglDisplay, mEglContext); 160 mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, 161 EGL10.EGL_NO_CONTEXT); 162 mEgl.eglTerminate(mEglDisplay); 163 mEglSurface = null; 164 mEglContext = null; 165 mEglDisplay = null; 166 mInputSurfaceTexture.release(); 167 mEglThread.quit(); 168 } 169 170 // Should be called from other thread. 171 public void sendMessageSync(int msg) { 172 mEglThreadBlockVar.close(); 173 sendEmptyMessage(msg); 174 mEglThreadBlockVar.block(); 175 } 176 177 } 178 179 public MosaicPreviewRenderer(SurfaceTexture tex, int w, int h, boolean isLandscape) { 180 mMosaicOutputSurfaceTexture = tex; 181 mWidth = w; 182 mHeight = h; 183 mIsLandscape = isLandscape; 184 185 mEglThread = new HandlerThread("PanoramaRealtimeRenderer"); 186 mEglThread.start(); 187 mEglHandler = new EGLHandler(mEglThread.getLooper()); 188 189 // We need to sync this because the generation of surface texture for input is 190 // done here and the client will continue with the assumption that the 191 // generation is completed. 192 mEglHandler.sendMessageSync(EGLHandler.MSG_INIT_EGL_SYNC); 193 } 194 195 public void release() { 196 mEglHandler.sendEmptyMessage(EGLHandler.MSG_RELEASE); 197 } 198 199 public void showPreviewFrameSync() { 200 mEglHandler.sendMessageSync(EGLHandler.MSG_SHOW_PREVIEW_FRAME_SYNC); 201 } 202 203 public void showPreviewFrame() { 204 mEglHandler.sendEmptyMessage(EGLHandler.MSG_SHOW_PREVIEW_FRAME); 205 } 206 207 public void alignFrame() { 208 mEglHandler.sendEmptyMessage(EGLHandler.MSG_ALIGN_FRAME); 209 } 210 211 public SurfaceTexture getInputSurfaceTexture() { 212 return mInputSurfaceTexture; 213 } 214 215 private void draw() { 216 MosaicRenderer.step(); 217 } 218 219 private static void checkEglError(String prompt, EGL10 egl) { 220 int error; 221 while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) { 222 Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error)); 223 } 224 } 225 226 private static final int EGL_OPENGL_ES2_BIT = 4; 227 private static final int[] CONFIG_SPEC = new int[] { 228 EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 229 EGL10.EGL_RED_SIZE, 8, 230 EGL10.EGL_GREEN_SIZE, 8, 231 EGL10.EGL_BLUE_SIZE, 8, 232 EGL10.EGL_NONE 233 }; 234 235 private static EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { 236 int[] numConfig = new int[1]; 237 if (!egl.eglChooseConfig(display, CONFIG_SPEC, null, 0, numConfig)) { 238 throw new IllegalArgumentException("eglChooseConfig failed"); 239 } 240 241 int numConfigs = numConfig[0]; 242 if (numConfigs <= 0) { 243 throw new IllegalArgumentException("No configs match configSpec"); 244 } 245 246 EGLConfig[] configs = new EGLConfig[numConfigs]; 247 if (!egl.eglChooseConfig( 248 display, CONFIG_SPEC, configs, numConfigs, numConfig)) { 249 throw new IllegalArgumentException("eglChooseConfig#2 failed"); 250 } 251 252 return configs[0]; 253 } 254} 255