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