/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.camera; import android.graphics.SurfaceTexture; import android.os.ConditionVariable; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import javax.microedition.khronos.opengles.GL10; public class MosaicPreviewRenderer { @SuppressWarnings("unused") private static final String TAG = "CAM_MosaicPreviewRenderer"; private int mWidth; // width of the view in UI private int mHeight; // height of the view in UI private boolean mIsLandscape = true; private int mOrientation = 0; private final float[] mTransformMatrix = new float[16]; private ConditionVariable mEglThreadBlockVar = new ConditionVariable(); private HandlerThread mEglThread; private MyHandler mHandler; private SurfaceTextureRenderer mSTRenderer; private SurfaceTexture mInputSurfaceTexture; private boolean mEnableWarpedPanoPreview = false; private class MyHandler extends Handler { public static final int MSG_INIT_SYNC = 0; public static final int MSG_SHOW_PREVIEW_FRAME_SYNC = 1; public static final int MSG_SHOW_PREVIEW_FRAME = 2; public static final int MSG_ALIGN_FRAME_SYNC = 3; public static final int MSG_RELEASE = 4; public static final int MSG_DO_PREVIEW_RESET = 5; public MyHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_INIT_SYNC: doInit(); mEglThreadBlockVar.open(); break; case MSG_SHOW_PREVIEW_FRAME_SYNC: doShowPreviewFrame(); mEglThreadBlockVar.open(); break; case MSG_DO_PREVIEW_RESET: doPreviewReset(); break; case MSG_SHOW_PREVIEW_FRAME: doShowPreviewFrame(); break; case MSG_ALIGN_FRAME_SYNC: doAlignFrame(); mEglThreadBlockVar.open(); break; case MSG_RELEASE: doRelease(); mEglThreadBlockVar.open(); break; } } private void doAlignFrame() { mInputSurfaceTexture.updateTexImage(); mInputSurfaceTexture.getTransformMatrix(mTransformMatrix); // Call setPreviewBackground to render high-res RGB textures to full screen. MosaicRenderer.setPreviewBackground(true); MosaicRenderer.preprocess(mTransformMatrix); MosaicRenderer.step(); MosaicRenderer.setPreviewBackground(false); MosaicRenderer.setWarping(true); MosaicRenderer.transferGPUtoCPU(); MosaicRenderer.updateMatrix(); MosaicRenderer.step(); } private void doShowPreviewFrame() { mInputSurfaceTexture.updateTexImage(); mInputSurfaceTexture.getTransformMatrix(mTransformMatrix); MosaicRenderer.setWarping(false); // Call preprocess to render it to low-res and high-res RGB textures. MosaicRenderer.preprocess(mTransformMatrix); MosaicRenderer.updateMatrix(); MosaicRenderer.step(); } private void doInit() { mInputSurfaceTexture = new SurfaceTexture(MosaicRenderer.init(mEnableWarpedPanoPreview)); MosaicRenderer.reset(mWidth, mHeight, mIsLandscape, mOrientation); } private void doPreviewReset() { MosaicRenderer.reset(mWidth, mHeight, mIsLandscape, mOrientation); } private void doRelease() { releaseSurfaceTexture(mInputSurfaceTexture); mEglThread.quit(); } private void releaseSurfaceTexture(SurfaceTexture st) { st.release(); } // Should be called from other thread. public void sendMessageSync(int msg) { mEglThreadBlockVar.close(); sendEmptyMessage(msg); mEglThreadBlockVar.block(); } } /** * Constructor. * * @param tex The {@link SurfaceTexture} for the final UI output. * @param w The width of the UI view. * @param h The height of the UI view. * @param isLandscape The UI orientation. {@code true} if in landscape, * false if in portrait. */ public MosaicPreviewRenderer(SurfaceTexture tex, int w, int h, boolean isLandscape, int orientation, boolean enableWarpedPanoPreview) { mIsLandscape = isLandscape; mOrientation = orientation; mEnableWarpedPanoPreview = enableWarpedPanoPreview; mEglThread = new HandlerThread("PanoramaRealtimeRenderer"); mEglThread.start(); mHandler = new MyHandler(mEglThread.getLooper()); mWidth = w; mHeight = h; SurfaceTextureRenderer.FrameDrawer dummy = new SurfaceTextureRenderer.FrameDrawer() { @Override public void onDrawFrame(GL10 gl) { // nothing, we have our draw functions. } }; mSTRenderer = new SurfaceTextureRenderer(tex, mHandler, dummy); // We need to sync this because the generation of surface texture for input is // done here and the client will continue with the assumption that the // generation is completed. mHandler.sendMessageSync(MyHandler.MSG_INIT_SYNC); } public void previewReset(int w, int h, boolean isLandscape, int orientation) { mWidth = w; mHeight = h; mIsLandscape = isLandscape; mOrientation = orientation; mHandler.sendEmptyMessage(MyHandler.MSG_DO_PREVIEW_RESET); mSTRenderer.draw(false); } public void release() { mSTRenderer.release(); mHandler.sendMessageSync(MyHandler.MSG_RELEASE); } public void showPreviewFrameSync() { mHandler.sendMessageSync(MyHandler.MSG_SHOW_PREVIEW_FRAME_SYNC); mSTRenderer.draw(true); } public void showPreviewFrame() { mHandler.sendEmptyMessage(MyHandler.MSG_SHOW_PREVIEW_FRAME); mSTRenderer.draw(false); } public void alignFrameSync() { mHandler.sendMessageSync(MyHandler.MSG_ALIGN_FRAME_SYNC); mSTRenderer.draw(true); } public SurfaceTexture getInputSurfaceTexture() { return mInputSurfaceTexture; } }