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