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