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