• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.graphics.SurfaceTexture;
20 import android.os.ConditionVariable;
21 import android.os.Handler;
22 import android.os.HandlerThread;
23 import android.os.Looper;
24 import android.os.Message;
25 import android.util.Log;
26 
27 import javax.microedition.khronos.egl.EGL10;
28 import javax.microedition.khronos.egl.EGLConfig;
29 import javax.microedition.khronos.egl.EGLContext;
30 import javax.microedition.khronos.egl.EGLDisplay;
31 import javax.microedition.khronos.egl.EGLSurface;
32 import javax.microedition.khronos.opengles.GL10;
33 
34 public 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 
EGLHandler(Looper looper)67         public EGLHandler(Looper looper) {
68             super(looper);
69         }
70 
71         @Override
handleMessage(Message msg)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 
doAlignFrame()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 
doShowPreviewFrame()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 
doInitGL()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 
doRelease()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.
sendMessageSync(int msg)171         public void sendMessageSync(int msg) {
172             mEglThreadBlockVar.close();
173             sendEmptyMessage(msg);
174             mEglThreadBlockVar.block();
175         }
176 
177     }
178 
MosaicPreviewRenderer(SurfaceTexture tex, int w, int h, boolean isLandscape)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 
release()195     public void release() {
196         mEglHandler.sendEmptyMessage(EGLHandler.MSG_RELEASE);
197     }
198 
showPreviewFrameSync()199     public void showPreviewFrameSync() {
200         mEglHandler.sendMessageSync(EGLHandler.MSG_SHOW_PREVIEW_FRAME_SYNC);
201     }
202 
showPreviewFrame()203     public void showPreviewFrame() {
204         mEglHandler.sendEmptyMessage(EGLHandler.MSG_SHOW_PREVIEW_FRAME);
205     }
206 
alignFrame()207     public void alignFrame() {
208         mEglHandler.sendEmptyMessage(EGLHandler.MSG_ALIGN_FRAME);
209     }
210 
getInputSurfaceTexture()211     public SurfaceTexture getInputSurfaceTexture() {
212         return mInputSurfaceTexture;
213     }
214 
draw()215     private void draw() {
216         MosaicRenderer.step();
217     }
218 
checkEglError(String prompt, EGL10 egl)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 
chooseConfig(EGL10 egl, EGLDisplay display)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