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