Home | History | Annotate | Download | only in camera
      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 
     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