1/*
2 * Copyright (C) 2013 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;
25
26import javax.microedition.khronos.opengles.GL10;
27
28public class MosaicPreviewRenderer {
29
30    @SuppressWarnings("unused")
31    private static final String TAG = "CAM_MosaicPreviewRenderer";
32
33    private int mWidth; // width of the view in UI
34    private int mHeight; // height of the view in UI
35
36    private boolean mIsLandscape = true;
37    private final float[] mTransformMatrix = new float[16];
38
39    private ConditionVariable mEglThreadBlockVar = new ConditionVariable();
40    private HandlerThread mEglThread;
41    private MyHandler mHandler;
42    private SurfaceTextureRenderer mSTRenderer;
43
44    private SurfaceTexture mInputSurfaceTexture;
45
46    private class MyHandler extends Handler {
47        public static final int MSG_INIT_SYNC = 0;
48        public static final int MSG_SHOW_PREVIEW_FRAME_SYNC = 1;
49        public static final int MSG_SHOW_PREVIEW_FRAME = 2;
50        public static final int MSG_ALIGN_FRAME_SYNC = 3;
51        public static final int MSG_RELEASE = 4;
52
53        public MyHandler(Looper looper) {
54            super(looper);
55        }
56
57        @Override
58        public void handleMessage(Message msg) {
59            switch (msg.what) {
60                case MSG_INIT_SYNC:
61                    doInit();
62                    mEglThreadBlockVar.open();
63                    break;
64                case MSG_SHOW_PREVIEW_FRAME_SYNC:
65                    doShowPreviewFrame();
66                    mEglThreadBlockVar.open();
67                    break;
68                case MSG_SHOW_PREVIEW_FRAME:
69                    doShowPreviewFrame();
70                    break;
71                case MSG_ALIGN_FRAME_SYNC:
72                    doAlignFrame();
73                    mEglThreadBlockVar.open();
74                    break;
75                case MSG_RELEASE:
76                    doRelease();
77                    mEglThreadBlockVar.open();
78                    break;
79            }
80        }
81
82        private void doAlignFrame() {
83            mInputSurfaceTexture.updateTexImage();
84            mInputSurfaceTexture.getTransformMatrix(mTransformMatrix);
85
86            MosaicRenderer.setWarping(true);
87            // Call preprocess to render it to low-res and high-res RGB textures.
88            MosaicRenderer.preprocess(mTransformMatrix);
89            // Now, transfer the textures from GPU to CPU memory for processing
90            MosaicRenderer.transferGPUtoCPU();
91            MosaicRenderer.updateMatrix();
92            MosaicRenderer.step();
93        }
94
95        private void doShowPreviewFrame() {
96            mInputSurfaceTexture.updateTexImage();
97            mInputSurfaceTexture.getTransformMatrix(mTransformMatrix);
98
99            MosaicRenderer.setWarping(false);
100            // Call preprocess to render it to low-res and high-res RGB textures.
101            MosaicRenderer.preprocess(mTransformMatrix);
102            MosaicRenderer.updateMatrix();
103            MosaicRenderer.step();
104        }
105
106        private void doInit() {
107            mInputSurfaceTexture = new SurfaceTexture(MosaicRenderer.init());
108            MosaicRenderer.reset(mWidth, mHeight, mIsLandscape);
109        }
110
111        private void doRelease() {
112            releaseSurfaceTexture(mInputSurfaceTexture);
113            mEglThread.quit();
114        }
115
116        private void releaseSurfaceTexture(SurfaceTexture st) {
117            st.release();
118        }
119
120        // Should be called from other thread.
121        public void sendMessageSync(int msg) {
122            mEglThreadBlockVar.close();
123            sendEmptyMessage(msg);
124            mEglThreadBlockVar.block();
125        }
126    }
127
128    /**
129     * Constructor.
130     *
131     * @param tex The {@link SurfaceTexture} for the final UI output.
132     * @param w The width of the UI view.
133     * @param h The height of the UI view.
134     * @param isLandscape The UI orientation. {@code true} if in landscape,
135     *                    false if in portrait.
136     */
137    public MosaicPreviewRenderer(SurfaceTexture tex, int w, int h, boolean isLandscape) {
138        mIsLandscape = isLandscape;
139
140        mEglThread = new HandlerThread("PanoramaRealtimeRenderer");
141        mEglThread.start();
142        mHandler = new MyHandler(mEglThread.getLooper());
143        mWidth = w;
144        mHeight = h;
145
146        SurfaceTextureRenderer.FrameDrawer dummy = new SurfaceTextureRenderer.FrameDrawer() {
147            @Override
148            public void onDrawFrame(GL10 gl) {
149                // nothing, we have our draw functions.
150            }
151        };
152        mSTRenderer = new SurfaceTextureRenderer(tex, mHandler, dummy);
153
154        // We need to sync this because the generation of surface texture for input is
155        // done here and the client will continue with the assumption that the
156        // generation is completed.
157        mHandler.sendMessageSync(MyHandler.MSG_INIT_SYNC);
158    }
159
160    public void release() {
161        mSTRenderer.release();
162        mHandler.sendMessageSync(MyHandler.MSG_RELEASE);
163    }
164
165    public void showPreviewFrameSync() {
166        mHandler.sendMessageSync(MyHandler.MSG_SHOW_PREVIEW_FRAME_SYNC);
167        mSTRenderer.draw(true);
168    }
169
170    public void showPreviewFrame() {
171        mHandler.sendEmptyMessage(MyHandler.MSG_SHOW_PREVIEW_FRAME);
172        mSTRenderer.draw(false);
173    }
174
175    public void alignFrameSync() {
176        mHandler.sendMessageSync(MyHandler.MSG_ALIGN_FRAME_SYNC);
177        mSTRenderer.draw(true);
178    }
179
180    public SurfaceTexture getInputSurfaceTexture() {
181        return mInputSurfaceTexture;
182    }
183}
184