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.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 
     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