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.panorama;
18 
19 import android.app.Activity;
20 import android.content.Context;
21 import android.content.pm.ActivityInfo;
22 import android.graphics.PixelFormat;
23 import android.opengl.GLSurfaceView;
24 import android.os.ConditionVariable;
25 import android.util.AttributeSet;
26 import android.util.Log;
27 
28 import javax.microedition.khronos.egl.EGL10;
29 import javax.microedition.khronos.egl.EGLConfig;
30 import javax.microedition.khronos.egl.EGLContext;
31 import javax.microedition.khronos.egl.EGLDisplay;
32 
33 public class MosaicRendererSurfaceView extends GLSurfaceView {
34     private static final String TAG = "MosaicRendererSurfaceView";
35     private static final boolean DEBUG = false;
36     private MosaicRendererSurfaceViewRenderer mRenderer;
37     private ConditionVariable mPreviewFrameReadyForProcessing;
38     private boolean mIsLandscapeOrientation = true;
39 
MosaicRendererSurfaceView(Context context)40     public MosaicRendererSurfaceView(Context context) {
41         super(context);
42         initialize(context, false, 0, 0);
43     }
44 
MosaicRendererSurfaceView(Context context, AttributeSet attrs)45     public MosaicRendererSurfaceView(Context context, AttributeSet attrs) {
46         super(context, attrs);
47         initialize(context, false, 0, 0);
48     }
49 
MosaicRendererSurfaceView(Context context, boolean translucent, int depth, int stencil)50     public MosaicRendererSurfaceView(Context context, boolean translucent,
51             int depth, int stencil) {
52         super(context);
53         initialize(context, translucent, depth, stencil);
54     }
55 
initialize(Context context, boolean translucent, int depth, int stencil)56     private void initialize(Context context, boolean translucent, int depth, int stencil) {
57         getDisplayOrientation(context);
58         init(translucent, depth, stencil);
59         setZOrderMediaOverlay(true);
60     }
61 
getDisplayOrientation(Context context)62     private void getDisplayOrientation(Context context) {
63         Activity activity = (PanoramaActivity) context;
64         mIsLandscapeOrientation = (activity.getRequestedOrientation()
65                 == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE );
66     }
67 
init(boolean translucent, int depth, int stencil)68     private void init(boolean translucent, int depth, int stencil) {
69 
70         /* By default, GLSurfaceView() creates a RGB_565 opaque surface.
71          * If we want a translucent one, we should change the surface's
72          * format here, using PixelFormat.TRANSLUCENT for GL Surfaces
73          * is interpreted as any 32-bit surface with alpha by SurfaceFlinger.
74          */
75         if (translucent) {
76             this.getHolder().setFormat(PixelFormat.TRANSLUCENT);
77         }
78 
79         /* Setup the context factory for 2.0 rendering.
80          * See ContextFactory class definition below
81          */
82         setEGLContextFactory(new ContextFactory());
83 
84         /* We need to choose an EGLConfig that matches the format of
85          * our surface exactly. This is going to be done in our
86          * custom config chooser. See ConfigChooser class definition
87          * below.
88          */
89         setEGLConfigChooser(
90             translucent ? new ConfigChooser(8, 8, 8, 8, depth, stencil) :
91             new ConfigChooser(5, 6, 5, 0, depth, stencil));
92 
93         /* Set the renderer responsible for frame rendering */
94         mRenderer = new MosaicRendererSurfaceViewRenderer(mIsLandscapeOrientation);
95         setRenderer(mRenderer);
96         setRenderMode(RENDERMODE_WHEN_DIRTY);
97         mPreviewFrameReadyForProcessing = new ConditionVariable();
98     }
99 
100     private static class ContextFactory implements GLSurfaceView.EGLContextFactory {
101         private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig)102         public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
103             Log.w(TAG, "creating OpenGL ES 2.0 context");
104             checkEglError("Before eglCreateContext", egl);
105             int[] attribList = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
106             EGLContext context = egl.eglCreateContext(
107                 display, eglConfig, EGL10.EGL_NO_CONTEXT, attribList);
108             checkEglError("After eglCreateContext", egl);
109             return context;
110         }
111 
destroyContext(EGL10 egl, EGLDisplay display, EGLContext context)112         public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {
113             egl.eglDestroyContext(display, context);
114         }
115     }
116 
checkEglError(String prompt, EGL10 egl)117     private static void checkEglError(String prompt, EGL10 egl) {
118         int error;
119         while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) {
120             Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error));
121         }
122     }
123 
124     private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser {
125 
ConfigChooser(int r, int g, int b, int a, int depth, int stencil)126         public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) {
127             mRedSize = r;
128             mGreenSize = g;
129             mBlueSize = b;
130             mAlphaSize = a;
131             mDepthSize = depth;
132             mStencilSize = stencil;
133         }
134 
135         /* This EGL config specification is used to specify 2.0 rendering.
136          * We use a minimum size of 4 bits for red/green/blue, but will
137          * perform actual matching in chooseConfig() below.
138          */
139         private static final int EGL_OPENGL_ES2_BIT = 4;
140         private static final int[] CONFIG_ATTRIBUTES =
141         {
142             EGL10.EGL_RED_SIZE, 4,
143             EGL10.EGL_GREEN_SIZE, 4,
144             EGL10.EGL_BLUE_SIZE, 4,
145             EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
146             EGL10.EGL_NONE
147         };
148 
chooseConfig(EGL10 egl, EGLDisplay display)149         public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
150 
151             /* Get the number of minimally matching EGL configurations
152              */
153             int[] numConfig = new int[1];
154             egl.eglChooseConfig(display, CONFIG_ATTRIBUTES, null, 0, numConfig);
155 
156             int numConfigs = numConfig[0];
157 
158             if (numConfigs <= 0) {
159                 throw new IllegalArgumentException("No configs match configSpec");
160             }
161 
162             /* Allocate then read the array of minimally matching EGL configs
163              */
164             EGLConfig[] configs = new EGLConfig[numConfigs];
165             egl.eglChooseConfig(display, CONFIG_ATTRIBUTES, configs, numConfigs, numConfig);
166 
167             if (DEBUG) {
168                  printConfigs(egl, display, configs);
169             }
170             /* Now return the "best" one
171              */
172             return chooseConfig(egl, display, configs);
173         }
174 
chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs)175         public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
176                 EGLConfig[] configs) {
177             for (EGLConfig config : configs) {
178                 int d = findConfigAttrib(egl, display, config,
179                         EGL10.EGL_DEPTH_SIZE, 0);
180                 int s = findConfigAttrib(egl, display, config,
181                         EGL10.EGL_STENCIL_SIZE, 0);
182 
183                 // We need at least mDepthSize and mStencilSize bits
184                 if (d < mDepthSize || s < mStencilSize)
185                     continue;
186 
187                 // We want an *exact* match for red/green/blue/alpha
188                 int r = findConfigAttrib(egl, display, config,
189                         EGL10.EGL_RED_SIZE, 0);
190                 int g = findConfigAttrib(egl, display, config,
191                             EGL10.EGL_GREEN_SIZE, 0);
192                 int b = findConfigAttrib(egl, display, config,
193                             EGL10.EGL_BLUE_SIZE, 0);
194                 int a = findConfigAttrib(egl, display, config,
195                         EGL10.EGL_ALPHA_SIZE, 0);
196 
197                 if (r == mRedSize && g == mGreenSize && b == mBlueSize && a == mAlphaSize)
198                     return config;
199             }
200             return null;
201         }
202 
findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config, int attribute, int defaultValue)203         private int findConfigAttrib(EGL10 egl, EGLDisplay display,
204                 EGLConfig config, int attribute, int defaultValue) {
205 
206             if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
207                 return mValue[0];
208             }
209             return defaultValue;
210         }
211 
printConfigs(EGL10 egl, EGLDisplay display, EGLConfig[] configs)212         private void printConfigs(EGL10 egl, EGLDisplay display,
213             EGLConfig[] configs) {
214             int numConfigs = configs.length;
215             Log.w(TAG, String.format("%d configurations", numConfigs));
216             for (int i = 0; i < numConfigs; i++) {
217                 Log.w(TAG, String.format("Configuration %d:\n", i));
218                 printConfig(egl, display, configs[i]);
219             }
220         }
221 
printConfig(EGL10 egl, EGLDisplay display, EGLConfig config)222         private void printConfig(EGL10 egl, EGLDisplay display,
223                 EGLConfig config) {
224             int[] attributes = {
225                     EGL10.EGL_BUFFER_SIZE,
226                     EGL10.EGL_ALPHA_SIZE,
227                     EGL10.EGL_BLUE_SIZE,
228                     EGL10.EGL_GREEN_SIZE,
229                     EGL10.EGL_RED_SIZE,
230                     EGL10.EGL_DEPTH_SIZE,
231                     EGL10.EGL_STENCIL_SIZE,
232                     EGL10.EGL_CONFIG_CAVEAT,
233                     EGL10.EGL_CONFIG_ID,
234                     EGL10.EGL_LEVEL,
235                     EGL10.EGL_MAX_PBUFFER_HEIGHT,
236                     EGL10.EGL_MAX_PBUFFER_PIXELS,
237                     EGL10.EGL_MAX_PBUFFER_WIDTH,
238                     EGL10.EGL_NATIVE_RENDERABLE,
239                     EGL10.EGL_NATIVE_VISUAL_ID,
240                     EGL10.EGL_NATIVE_VISUAL_TYPE,
241                     0x3030, // EGL10.EGL_PRESERVED_RESOURCES,
242                     EGL10.EGL_SAMPLES,
243                     EGL10.EGL_SAMPLE_BUFFERS,
244                     EGL10.EGL_SURFACE_TYPE,
245                     EGL10.EGL_TRANSPARENT_TYPE,
246                     EGL10.EGL_TRANSPARENT_RED_VALUE,
247                     EGL10.EGL_TRANSPARENT_GREEN_VALUE,
248                     EGL10.EGL_TRANSPARENT_BLUE_VALUE,
249                     0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB,
250                     0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA,
251                     0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL,
252                     0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL,
253                     EGL10.EGL_LUMINANCE_SIZE,
254                     EGL10.EGL_ALPHA_MASK_SIZE,
255                     EGL10.EGL_COLOR_BUFFER_TYPE,
256                     EGL10.EGL_RENDERABLE_TYPE,
257                     0x3042 // EGL10.EGL_CONFORMANT
258             };
259             String[] names = {
260                     "EGL_BUFFER_SIZE",
261                     "EGL_ALPHA_SIZE",
262                     "EGL_BLUE_SIZE",
263                     "EGL_GREEN_SIZE",
264                     "EGL_RED_SIZE",
265                     "EGL_DEPTH_SIZE",
266                     "EGL_STENCIL_SIZE",
267                     "EGL_CONFIG_CAVEAT",
268                     "EGL_CONFIG_ID",
269                     "EGL_LEVEL",
270                     "EGL_MAX_PBUFFER_HEIGHT",
271                     "EGL_MAX_PBUFFER_PIXELS",
272                     "EGL_MAX_PBUFFER_WIDTH",
273                     "EGL_NATIVE_RENDERABLE",
274                     "EGL_NATIVE_VISUAL_ID",
275                     "EGL_NATIVE_VISUAL_TYPE",
276                     "EGL_PRESERVED_RESOURCES",
277                     "EGL_SAMPLES",
278                     "EGL_SAMPLE_BUFFERS",
279                     "EGL_SURFACE_TYPE",
280                     "EGL_TRANSPARENT_TYPE",
281                     "EGL_TRANSPARENT_RED_VALUE",
282                     "EGL_TRANSPARENT_GREEN_VALUE",
283                     "EGL_TRANSPARENT_BLUE_VALUE",
284                     "EGL_BIND_TO_TEXTURE_RGB",
285                     "EGL_BIND_TO_TEXTURE_RGBA",
286                     "EGL_MIN_SWAP_INTERVAL",
287                     "EGL_MAX_SWAP_INTERVAL",
288                     "EGL_LUMINANCE_SIZE",
289                     "EGL_ALPHA_MASK_SIZE",
290                     "EGL_COLOR_BUFFER_TYPE",
291                     "EGL_RENDERABLE_TYPE",
292                     "EGL_CONFORMANT"
293             };
294             int[] value = new int[1];
295             for (int i = 0; i < attributes.length; i++) {
296                 int attribute = attributes[i];
297                 String name = names[i];
298                 if (egl.eglGetConfigAttrib(display, config, attribute, value)) {
299                     Log.w(TAG, String.format("  %s: %d\n", name, value[0]));
300                 } else {
301                     // Log.w(TAG, String.format("  %s: failed\n", name));
302                     while (egl.eglGetError() != EGL10.EGL_SUCCESS);
303                 }
304             }
305         }
306 
307         // Subclasses can adjust these values:
308         protected int mRedSize;
309         protected int mGreenSize;
310         protected int mBlueSize;
311         protected int mAlphaSize;
312         protected int mDepthSize;
313         protected int mStencilSize;
314         private int[] mValue = new int[1];
315     }
316 
lockPreviewReadyFlag()317     public void lockPreviewReadyFlag() {
318         mPreviewFrameReadyForProcessing.close();
319     }
320 
unlockPreviewReadyFlag()321     private void unlockPreviewReadyFlag() {
322         mPreviewFrameReadyForProcessing.open();
323     }
324 
waitUntilPreviewReady()325     public void waitUntilPreviewReady() {
326         mPreviewFrameReadyForProcessing.block();
327     }
328 
setReady()329     public void setReady() {
330         queueEvent(new Runnable() {
331 
332             @Override
333             public void run() {
334                 mRenderer.setReady();
335             }
336         });
337     }
338 
preprocess(final float[] transformMatrix)339     public void preprocess(final float[] transformMatrix) {
340         queueEvent(new Runnable() {
341 
342             @Override
343             public void run() {
344                 mRenderer.preprocess(transformMatrix);
345             }
346         });
347     }
348 
transferGPUtoCPU()349     public void transferGPUtoCPU() {
350         queueEvent(new Runnable() {
351 
352             @Override
353             public void run() {
354                 mRenderer.transferGPUtoCPU();
355                 unlockPreviewReadyFlag();
356             }
357         });
358     }
359 
setWarping(final boolean flag)360     public void setWarping(final boolean flag) {
361         queueEvent(new Runnable() {
362 
363             @Override
364             public void run() {
365                 mRenderer.setWarping(flag);
366             }
367         });
368     }
369 
getRenderer()370     public MosaicRendererSurfaceViewRenderer getRenderer() {
371         return mRenderer;
372     }
373 
374 }
375