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