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                     break;
     94             }
     95         }
     96 
     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 
    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 
    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 
    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)
    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.
    181         public void sendMessageSync(int msg) {
    182             mEglThreadBlockVar.close();
    183             sendEmptyMessage(msg);
    184             mEglThreadBlockVar.block();
    185         }
    186 
    187     }
    188 
    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 
    205     public void release() {
    206         mEglHandler.sendEmptyMessage(EGLHandler.MSG_RELEASE);
    207     }
    208 
    209     public void showPreviewFrameSync() {
    210         mEglHandler.sendMessageSync(EGLHandler.MSG_SHOW_PREVIEW_FRAME_SYNC);
    211     }
    212 
    213     public void showPreviewFrame() {
    214         mEglHandler.sendEmptyMessage(EGLHandler.MSG_SHOW_PREVIEW_FRAME);
    215     }
    216 
    217     public void alignFrameSync() {
    218         mEglHandler.sendMessageSync(EGLHandler.MSG_ALIGN_FRAME_SYNC);
    219     }
    220 
    221     public SurfaceTexture getInputSurfaceTexture() {
    222         return mInputSurfaceTexture;
    223     }
    224 
    225     private void draw() {
    226         MosaicRenderer.step();
    227     }
    228 
    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 
    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