blob: 9a536485ae1b924f4ebf556562ff05cb9d33b676 [file] [log] [blame]
Jeff Brown96307042012-07-27 15:51:34 -07001/*
2 * Copyright (C) 2012 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.server.power;
18
19import android.graphics.Bitmap;
20import android.graphics.PixelFormat;
21import android.opengl.EGL14;
22import android.opengl.EGLConfig;
23import android.opengl.EGLContext;
24import android.opengl.EGLDisplay;
25import android.opengl.EGLSurface;
26import android.opengl.GLES10;
27import android.opengl.GLUtils;
28import android.os.Looper;
Jeff Brown96307042012-07-27 15:51:34 -070029import android.util.FloatMath;
30import android.util.Slog;
31import android.view.Display;
32import android.view.DisplayInfo;
33import android.view.Surface;
34import android.view.SurfaceSession;
Jeff Brown96307042012-07-27 15:51:34 -070035
36import java.io.PrintWriter;
37import java.nio.ByteBuffer;
38import java.nio.ByteOrder;
39import java.nio.FloatBuffer;
40
41/**
42 * Bzzzoooop! *crackle*
Jeff Brown8b9cf1c2012-10-07 14:54:17 -070043 * <p>
Jeff Brown96307042012-07-27 15:51:34 -070044 * Animates a screen transition from on to off or off to on by applying
45 * some GL transformations to a screenshot.
Jeff Brown8b9cf1c2012-10-07 14:54:17 -070046 * </p><p>
Jeff Brown96307042012-07-27 15:51:34 -070047 * This component must only be created or accessed by the {@link Looper} thread
48 * that belongs to the {@link DisplayPowerController}.
Jeff Brown8b9cf1c2012-10-07 14:54:17 -070049 * </p>
Jeff Brown96307042012-07-27 15:51:34 -070050 */
51final class ElectronBeam {
52 private static final String TAG = "ElectronBeam";
53
54 private static final boolean DEBUG = false;
55
56 // The layer for the electron beam surface.
57 // This is currently hardcoded to be one layer above the boot animation.
58 private static final int ELECTRON_BEAM_LAYER = 0x40000001;
59
60 // The relative proportion of the animation to spend performing
61 // the horizontal stretch effect. The remainder is spent performing
62 // the vertical stretch effect.
Jeff Brown3c584f22012-10-10 14:36:00 -070063 private static final float HSTRETCH_DURATION = 0.5f;
Jeff Brown96307042012-07-27 15:51:34 -070064 private static final float VSTRETCH_DURATION = 1.0f - HSTRETCH_DURATION;
65
Jeff Brown78eb1222012-10-10 18:27:44 -070066 // The number of frames to draw when preparing the animation so that it will
67 // be ready to run smoothly. We use 3 frames because we are triple-buffered.
68 // See code for details.
69 private static final int DEJANK_FRAMES = 3;
70
Jeff Brown96307042012-07-27 15:51:34 -070071 // Set to true when the animation context has been fully prepared.
72 private boolean mPrepared;
Jeff Brown8b9cf1c2012-10-07 14:54:17 -070073 private int mMode;
Jeff Brown96307042012-07-27 15:51:34 -070074
Jeff Brown98365d72012-08-19 20:30:52 -070075 private final Display mDisplay;
Jeff Brown96307042012-07-27 15:51:34 -070076 private final DisplayInfo mDisplayInfo = new DisplayInfo();
77 private int mDisplayLayerStack; // layer stack associated with primary display
78 private int mDisplayRotation;
79 private int mDisplayWidth; // real width, not rotated
80 private int mDisplayHeight; // real height, not rotated
81 private SurfaceSession mSurfaceSession;
82 private Surface mSurface;
83 private EGLDisplay mEglDisplay;
84 private EGLConfig mEglConfig;
85 private EGLContext mEglContext;
86 private EGLSurface mEglSurface;
87 private boolean mSurfaceVisible;
Jeff Brown252c2062012-10-08 16:21:01 -070088 private float mSurfaceAlpha;
Jeff Brown96307042012-07-27 15:51:34 -070089
90 // Texture names. We only use one texture, which contains the screenshot.
91 private final int[] mTexNames = new int[1];
92 private boolean mTexNamesGenerated;
93
94 // Vertex and corresponding texture coordinates.
95 // We have 4 2D vertices, so 8 elements. The vertices form a quad.
96 private final FloatBuffer mVertexBuffer = createNativeFloatBuffer(8);
97 private final FloatBuffer mTexCoordBuffer = createNativeFloatBuffer(8);
98
Jeff Brown252c2062012-10-08 16:21:01 -070099 /**
100 * Animates an electron beam warming up.
101 */
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700102 public static final int MODE_WARM_UP = 0;
Jeff Brown252c2062012-10-08 16:21:01 -0700103
104 /**
105 * Animates an electron beam shutting off.
106 */
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700107 public static final int MODE_COOL_DOWN = 1;
Jeff Brown252c2062012-10-08 16:21:01 -0700108
109 /**
110 * Animates a simple dim layer to fade the contents of the screen in or out progressively.
111 */
112 public static final int MODE_FADE = 2;
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700113
Jeff Brown98365d72012-08-19 20:30:52 -0700114 public ElectronBeam(Display display) {
115 mDisplay = display;
Jeff Brown96307042012-07-27 15:51:34 -0700116 }
117
118 /**
119 * Warms up the electron beam in preparation for turning on or off.
120 * This method prepares a GL context, and captures a screen shot.
121 *
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700122 * @param mode The desired mode for the upcoming animation.
Jeff Brown96307042012-07-27 15:51:34 -0700123 * @return True if the electron beam is ready, false if it is uncontrollable.
124 */
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700125 public boolean prepare(int mode) {
Jeff Brown96307042012-07-27 15:51:34 -0700126 if (DEBUG) {
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700127 Slog.d(TAG, "prepare: mode=" + mode);
Jeff Brown96307042012-07-27 15:51:34 -0700128 }
129
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700130 mMode = mode;
Jeff Brown96307042012-07-27 15:51:34 -0700131
132 // Get the display size and adjust it for rotation.
Jeff Brown98365d72012-08-19 20:30:52 -0700133 mDisplay.getDisplayInfo(mDisplayInfo);
134 mDisplayLayerStack = mDisplay.getLayerStack();
Jeff Brown96307042012-07-27 15:51:34 -0700135 mDisplayRotation = mDisplayInfo.rotation;
136 if (mDisplayRotation == Surface.ROTATION_90
137 || mDisplayRotation == Surface.ROTATION_270) {
138 mDisplayWidth = mDisplayInfo.logicalHeight;
139 mDisplayHeight = mDisplayInfo.logicalWidth;
140 } else {
141 mDisplayWidth = mDisplayInfo.logicalWidth;
142 mDisplayHeight = mDisplayInfo.logicalHeight;
143 }
144
145 // Prepare the surface for drawing.
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700146 if (!tryPrepare()) {
Jeff Brown96307042012-07-27 15:51:34 -0700147 dismiss();
148 return false;
149 }
150
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700151 // Done.
Jeff Brown96307042012-07-27 15:51:34 -0700152 mPrepared = true;
Jeff Brown78eb1222012-10-10 18:27:44 -0700153
154 // Dejanking optimization.
155 // Some GL drivers can introduce a lot of lag in the first few frames as they
156 // initialize their state and allocate graphics buffers for rendering.
157 // Work around this problem by rendering the first frame of the animation a few
158 // times. The rest of the animation should run smoothly thereafter.
159 // The frames we draw here aren't visible because we are essentially just
160 // painting the screenshot as-is.
161 if (mode == MODE_COOL_DOWN) {
162 for (int i = 0; i < DEJANK_FRAMES; i++) {
163 draw(1.0f);
164 }
165 }
Jeff Brown96307042012-07-27 15:51:34 -0700166 return true;
167 }
168
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700169 private boolean tryPrepare() {
170 if (createSurface()) {
Jeff Brown252c2062012-10-08 16:21:01 -0700171 if (mMode == MODE_FADE) {
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700172 return true;
173 }
174 return createEglContext()
175 && createEglSurface()
176 && captureScreenshotTextureAndSetViewport();
177 }
178 return false;
179 }
180
Jeff Brown96307042012-07-27 15:51:34 -0700181 /**
182 * Dismisses the electron beam animation surface and cleans up.
183 *
184 * To prevent stray photons from leaking out after the electron beam has been
185 * turned off, it is a good idea to defer dismissing the animation until the
186 * electron beam has been turned back on fully.
187 */
188 public void dismiss() {
189 if (DEBUG) {
190 Slog.d(TAG, "dismiss");
191 }
192
193 destroyScreenshotTexture();
194 destroyEglSurface();
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700195 destroySurface();
Jeff Brown96307042012-07-27 15:51:34 -0700196 mPrepared = false;
197 }
198
199 /**
200 * Draws an animation frame showing the electron beam activated at the
201 * specified level.
202 *
203 * @param level The electron beam level.
204 * @return True if successful.
205 */
206 public boolean draw(float level) {
207 if (DEBUG) {
208 Slog.d(TAG, "drawFrame: level=" + level);
209 }
210
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700211 if (!mPrepared) {
212 return false;
213 }
214
Jeff Brown252c2062012-10-08 16:21:01 -0700215 if (mMode == MODE_FADE) {
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700216 return showSurface(1.0f - level);
217 }
218
Jeff Brown96307042012-07-27 15:51:34 -0700219 if (!attachEglContext()) {
220 return false;
221 }
222 try {
223 // Clear frame to solid black.
224 GLES10.glClearColor(0f, 0f, 0f, 1f);
225 GLES10.glClear(GLES10.GL_COLOR_BUFFER_BIT);
226
227 // Draw the frame.
228 if (level < HSTRETCH_DURATION) {
229 drawHStretch(1.0f - (level / HSTRETCH_DURATION));
230 } else {
231 drawVStretch(1.0f - ((level - HSTRETCH_DURATION) / VSTRETCH_DURATION));
232 }
233 if (checkGlErrors("drawFrame")) {
234 return false;
235 }
236
237 EGL14.eglSwapBuffers(mEglDisplay, mEglSurface);
238 } finally {
239 detachEglContext();
240 }
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700241 return showSurface(1.0f);
Jeff Brown96307042012-07-27 15:51:34 -0700242 }
243
244 /**
245 * Draws a frame where the content of the electron beam is collapsing inwards upon
246 * itself vertically with red / green / blue channels dispersing and eventually
247 * merging down to a single horizontal line.
248 *
249 * @param stretch The stretch factor. 0.0 is no collapse, 1.0 is full collapse.
250 */
251 private void drawVStretch(float stretch) {
252 // compute interpolation scale factors for each color channel
253 final float ar = scurve(stretch, 7.5f);
254 final float ag = scurve(stretch, 8.0f);
255 final float ab = scurve(stretch, 8.5f);
256 if (DEBUG) {
257 Slog.d(TAG, "drawVStretch: stretch=" + stretch
258 + ", ar=" + ar + ", ag=" + ag + ", ab=" + ab);
259 }
260
261 // set blending
262 GLES10.glBlendFunc(GLES10.GL_ONE, GLES10.GL_ONE);
263 GLES10.glEnable(GLES10.GL_BLEND);
264
265 // bind vertex buffer
266 GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer);
267 GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY);
268
269 // bind texture and set blending for drawing planes
270 GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, mTexNames[0]);
271 GLES10.glTexEnvx(GLES10.GL_TEXTURE_ENV, GLES10.GL_TEXTURE_ENV_MODE,
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700272 mMode == MODE_WARM_UP ? GLES10.GL_MODULATE : GLES10.GL_REPLACE);
Jeff Brown96307042012-07-27 15:51:34 -0700273 GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
274 GLES10.GL_TEXTURE_MAG_FILTER, GLES10.GL_LINEAR);
275 GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
276 GLES10.GL_TEXTURE_MIN_FILTER, GLES10.GL_LINEAR);
277 GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
278 GLES10.GL_TEXTURE_WRAP_S, GLES10.GL_CLAMP_TO_EDGE);
279 GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
280 GLES10.GL_TEXTURE_WRAP_T, GLES10.GL_CLAMP_TO_EDGE);
281 GLES10.glEnable(GLES10.GL_TEXTURE_2D);
282 GLES10.glTexCoordPointer(2, GLES10.GL_FLOAT, 0, mTexCoordBuffer);
283 GLES10.glEnableClientState(GLES10.GL_TEXTURE_COORD_ARRAY);
284
285 // draw the red plane
286 setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ar);
287 GLES10.glColorMask(true, false, false, true);
288 GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
289
290 // draw the green plane
291 setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag);
292 GLES10.glColorMask(false, true, false, true);
293 GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
294
295 // draw the blue plane
296 setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ab);
297 GLES10.glColorMask(false, false, true, true);
298 GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
299
300 // clean up after drawing planes
301 GLES10.glDisable(GLES10.GL_TEXTURE_2D);
302 GLES10.glDisableClientState(GLES10.GL_TEXTURE_COORD_ARRAY);
303 GLES10.glColorMask(true, true, true, true);
304
305 // draw the white highlight (we use the last vertices)
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700306 if (mMode == MODE_COOL_DOWN) {
Jeff Brown96307042012-07-27 15:51:34 -0700307 GLES10.glColor4f(ag, ag, ag, 1.0f);
308 GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
309 }
310
311 // clean up
312 GLES10.glDisableClientState(GLES10.GL_VERTEX_ARRAY);
313 GLES10.glDisable(GLES10.GL_BLEND);
314 }
315
316 /**
317 * Draws a frame where the electron beam has been stretched out into
318 * a thin white horizontal line that fades as it expands outwards.
319 *
320 * @param stretch The stretch factor. 0.0 is no stretch / no fade,
321 * 1.0 is maximum stretch / maximum fade.
322 */
323 private void drawHStretch(float stretch) {
324 // compute interpolation scale factor
325 final float ag = scurve(stretch, 8.0f);
326 if (DEBUG) {
327 Slog.d(TAG, "drawHStretch: stretch=" + stretch + ", ag=" + ag);
328 }
329
330 if (stretch < 1.0f) {
331 // bind vertex buffer
332 GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer);
333 GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY);
334
335 // draw narrow fading white line
336 setHStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag);
337 GLES10.glColor4f(1.0f - ag, 1.0f - ag, 1.0f - ag, 1.0f);
338 GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
339
340 // clean up
341 GLES10.glDisableClientState(GLES10.GL_VERTEX_ARRAY);
342 }
343 }
344
345 private static void setVStretchQuad(FloatBuffer vtx, float dw, float dh, float a) {
346 final float w = dw + (dw * a);
347 final float h = dh - (dh * a);
348 final float x = (dw - w) * 0.5f;
349 final float y = (dh - h) * 0.5f;
350 setQuad(vtx, x, y, w, h);
351 }
352
353 private static void setHStretchQuad(FloatBuffer vtx, float dw, float dh, float a) {
354 final float w = dw + (dw * a);
355 final float h = 1.0f;
356 final float x = (dw - w) * 0.5f;
357 final float y = (dh - h) * 0.5f;
358 setQuad(vtx, x, y, w, h);
359 }
360
361 private static void setQuad(FloatBuffer vtx, float x, float y, float w, float h) {
362 if (DEBUG) {
363 Slog.d(TAG, "setQuad: x=" + x + ", y=" + y + ", w=" + w + ", h=" + h);
364 }
365 vtx.put(0, x);
366 vtx.put(1, y);
367 vtx.put(2, x);
368 vtx.put(3, y + h);
369 vtx.put(4, x + w);
370 vtx.put(5, y + h);
371 vtx.put(6, x + w);
372 vtx.put(7, y);
373 }
374
375 private boolean captureScreenshotTextureAndSetViewport() {
376 // TODO: Use a SurfaceTexture to avoid the extra texture upload.
377 Bitmap bitmap = Surface.screenshot(mDisplayWidth, mDisplayHeight,
378 0, ELECTRON_BEAM_LAYER - 1);
379 if (bitmap == null) {
380 Slog.e(TAG, "Could not take a screenshot!");
381 return false;
382 }
383 try {
384 if (!attachEglContext()) {
385 return false;
386 }
387 try {
388 if (!mTexNamesGenerated) {
389 GLES10.glGenTextures(1, mTexNames, 0);
390 if (checkGlErrors("glGenTextures")) {
391 return false;
392 }
393 mTexNamesGenerated = true;
394 }
395
396 GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, mTexNames[0]);
397 if (checkGlErrors("glBindTexture")) {
398 return false;
399 }
400
401 float u = 1.0f;
402 float v = 1.0f;
403 GLUtils.texImage2D(GLES10.GL_TEXTURE_2D, 0, bitmap, 0);
404 if (checkGlErrors("glTexImage2D, first try", false)) {
405 // Try a power of two size texture instead.
406 int tw = nextPowerOfTwo(mDisplayWidth);
407 int th = nextPowerOfTwo(mDisplayHeight);
408 int format = GLUtils.getInternalFormat(bitmap);
409 GLES10.glTexImage2D(GLES10.GL_TEXTURE_2D, 0,
410 format, tw, th, 0,
411 format, GLES10.GL_UNSIGNED_BYTE, null);
412 if (checkGlErrors("glTexImage2D, second try")) {
413 return false;
414 }
415
416 GLUtils.texSubImage2D(GLES10.GL_TEXTURE_2D, 0, 0, 0, bitmap);
417 if (checkGlErrors("glTexSubImage2D")) {
418 return false;
419 }
420
421 u = (float)mDisplayWidth / tw;
422 v = (float)mDisplayHeight / th;
423 }
424
425 // Set up texture coordinates for a quad.
426 // We might need to change this if the texture ends up being
427 // a different size from the display for some reason.
428 mTexCoordBuffer.put(0, 0f);
429 mTexCoordBuffer.put(1, v);
430 mTexCoordBuffer.put(2, 0f);
431 mTexCoordBuffer.put(3, 0f);
432 mTexCoordBuffer.put(4, u);
433 mTexCoordBuffer.put(5, 0f);
434 mTexCoordBuffer.put(6, u);
435 mTexCoordBuffer.put(7, v);
436
437 // Set up our viewport.
438 GLES10.glViewport(0, 0, mDisplayWidth, mDisplayHeight);
439 GLES10.glMatrixMode(GLES10.GL_PROJECTION);
440 GLES10.glLoadIdentity();
441 GLES10.glOrthof(0, mDisplayWidth, 0, mDisplayHeight, 0, 1);
442 GLES10.glMatrixMode(GLES10.GL_MODELVIEW);
443 GLES10.glLoadIdentity();
444 GLES10.glMatrixMode(GLES10.GL_TEXTURE);
445 GLES10.glLoadIdentity();
446 } finally {
447 detachEglContext();
448 }
449 } finally {
450 bitmap.recycle();
451 }
452 return true;
453 }
454
455 private void destroyScreenshotTexture() {
456 if (mTexNamesGenerated) {
457 mTexNamesGenerated = false;
458 if (attachEglContext()) {
459 try {
460 GLES10.glDeleteTextures(1, mTexNames, 0);
461 checkGlErrors("glDeleteTextures");
462 } finally {
463 detachEglContext();
464 }
465 }
466 }
467 }
468
469 private boolean createEglContext() {
470 if (mEglDisplay == null) {
471 mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
472 if (mEglDisplay == EGL14.EGL_NO_DISPLAY) {
473 logEglError("eglGetDisplay");
474 return false;
475 }
476
477 int[] version = new int[2];
478 if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) {
479 mEglDisplay = null;
480 logEglError("eglInitialize");
481 return false;
482 }
483 }
484
485 if (mEglConfig == null) {
486 int[] eglConfigAttribList = new int[] {
487 EGL14.EGL_RED_SIZE, 8,
488 EGL14.EGL_GREEN_SIZE, 8,
489 EGL14.EGL_BLUE_SIZE, 8,
490 EGL14.EGL_ALPHA_SIZE, 8,
491 EGL14.EGL_NONE
492 };
493 int[] numEglConfigs = new int[1];
494 EGLConfig[] eglConfigs = new EGLConfig[1];
495 if (!EGL14.eglChooseConfig(mEglDisplay, eglConfigAttribList, 0,
496 eglConfigs, 0, eglConfigs.length, numEglConfigs, 0)) {
497 logEglError("eglChooseConfig");
498 return false;
499 }
500 mEglConfig = eglConfigs[0];
501 }
502
503 if (mEglContext == null) {
504 int[] eglContextAttribList = new int[] {
505 EGL14.EGL_NONE
506 };
507 mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig,
508 EGL14.EGL_NO_CONTEXT, eglContextAttribList, 0);
509 if (mEglContext == null) {
510 logEglError("eglCreateContext");
511 return false;
512 }
513 }
514 return true;
515 }
516
517 /* not used because it is too expensive to create / destroy contexts all of the time
518 private void destroyEglContext() {
519 if (mEglContext != null) {
520 if (!EGL14.eglDestroyContext(mEglDisplay, mEglContext)) {
521 logEglError("eglDestroyContext");
522 }
523 mEglContext = null;
524 }
525 }*/
526
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700527 private boolean createSurface() {
Jeff Brown96307042012-07-27 15:51:34 -0700528 if (mSurfaceSession == null) {
529 mSurfaceSession = new SurfaceSession();
530 }
531
532 Surface.openTransaction();
533 try {
534 if (mSurface == null) {
535 try {
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700536 int flags;
Jeff Brown252c2062012-10-08 16:21:01 -0700537 if (mMode == MODE_FADE) {
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700538 flags = Surface.FX_SURFACE_DIM | Surface.HIDDEN;
539 } else {
540 flags = Surface.OPAQUE | Surface.HIDDEN;
541 }
Jeff Brown64a55af2012-08-26 02:47:39 -0700542 mSurface = new Surface(mSurfaceSession,
543 "ElectronBeam", mDisplayWidth, mDisplayHeight,
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700544 PixelFormat.OPAQUE, flags);
Jeff Brown96307042012-07-27 15:51:34 -0700545 } catch (Surface.OutOfResourcesException ex) {
546 Slog.e(TAG, "Unable to create surface.", ex);
547 return false;
548 }
549 }
550
Jeff Brown64a55af2012-08-26 02:47:39 -0700551 mSurface.setLayerStack(mDisplayLayerStack);
Jeff Brown96307042012-07-27 15:51:34 -0700552 mSurface.setSize(mDisplayWidth, mDisplayHeight);
553
554 switch (mDisplayRotation) {
555 case Surface.ROTATION_0:
556 mSurface.setPosition(0, 0);
557 mSurface.setMatrix(1, 0, 0, 1);
558 break;
559 case Surface.ROTATION_90:
560 mSurface.setPosition(0, mDisplayWidth);
561 mSurface.setMatrix(0, -1, 1, 0);
562 break;
563 case Surface.ROTATION_180:
564 mSurface.setPosition(mDisplayWidth, mDisplayHeight);
565 mSurface.setMatrix(-1, 0, 0, -1);
566 break;
567 case Surface.ROTATION_270:
568 mSurface.setPosition(mDisplayHeight, 0);
569 mSurface.setMatrix(0, 1, -1, 0);
570 break;
571 }
572 } finally {
573 Surface.closeTransaction();
574 }
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700575 return true;
576 }
Jeff Brown96307042012-07-27 15:51:34 -0700577
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700578 private boolean createEglSurface() {
Jeff Brown96307042012-07-27 15:51:34 -0700579 if (mEglSurface == null) {
580 int[] eglSurfaceAttribList = new int[] {
581 EGL14.EGL_NONE
582 };
583 mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface,
584 eglSurfaceAttribList, 0);
585 if (mEglSurface == null) {
586 logEglError("eglCreateWindowSurface");
587 return false;
588 }
589 }
590 return true;
591 }
592
593 private void destroyEglSurface() {
594 if (mEglSurface != null) {
595 if (!EGL14.eglDestroySurface(mEglDisplay, mEglSurface)) {
596 logEglError("eglDestroySurface");
597 }
598 mEglSurface = null;
599 }
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700600 }
Jeff Brown96307042012-07-27 15:51:34 -0700601
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700602 private void destroySurface() {
Jeff Brown96307042012-07-27 15:51:34 -0700603 if (mSurface != null) {
604 Surface.openTransaction();
605 try {
606 mSurface.destroy();
607 } finally {
608 Surface.closeTransaction();
609 }
610 mSurface = null;
611 mSurfaceVisible = false;
Jeff Brown252c2062012-10-08 16:21:01 -0700612 mSurfaceAlpha = 0f;
Jeff Brown96307042012-07-27 15:51:34 -0700613 }
614 }
615
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700616 private boolean showSurface(float alpha) {
Jeff Brown252c2062012-10-08 16:21:01 -0700617 if (!mSurfaceVisible || mSurfaceAlpha != alpha) {
Jeff Brown96307042012-07-27 15:51:34 -0700618 Surface.openTransaction();
619 try {
620 mSurface.setLayer(ELECTRON_BEAM_LAYER);
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700621 mSurface.setAlpha(alpha);
Jeff Brown96307042012-07-27 15:51:34 -0700622 mSurface.show();
623 } finally {
624 Surface.closeTransaction();
625 }
626 mSurfaceVisible = true;
Jeff Brown252c2062012-10-08 16:21:01 -0700627 mSurfaceAlpha = alpha;
Jeff Brown96307042012-07-27 15:51:34 -0700628 }
629 return true;
630 }
631
632 private boolean attachEglContext() {
633 if (mEglSurface == null) {
634 return false;
635 }
636 if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
637 logEglError("eglMakeCurrent");
638 return false;
639 }
640 return true;
641 }
642
643 private void detachEglContext() {
644 if (mEglDisplay != null) {
645 EGL14.eglMakeCurrent(mEglDisplay,
646 EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
647 }
648 }
649
650 /**
651 * Interpolates a value in the range 0 .. 1 along a sigmoid curve
652 * yielding a result in the range 0 .. 1 scaled such that:
653 * scurve(0) == 0, scurve(0.5) == 0.5, scurve(1) == 1.
654 */
655 private static float scurve(float value, float s) {
656 // A basic sigmoid has the form y = 1.0f / FloatMap.exp(-x * s).
657 // Here we take the input datum and shift it by 0.5 so that the
658 // domain spans the range -0.5 .. 0.5 instead of 0 .. 1.
659 final float x = value - 0.5f;
660
661 // Next apply the sigmoid function to the scaled value
662 // which produces a value in the range 0 .. 1 so we subtract
663 // 0.5 to get a value in the range -0.5 .. 0.5 instead.
664 final float y = sigmoid(x, s) - 0.5f;
665
666 // To obtain the desired boundary conditions we need to scale
667 // the result so that it fills a range of -1 .. 1.
668 final float v = sigmoid(0.5f, s) - 0.5f;
669
670 // And finally remap the value back to a range of 0 .. 1.
671 return y / v * 0.5f + 0.5f;
672 }
673
674 private static float sigmoid(float x, float s) {
675 return 1.0f / (1.0f + FloatMath.exp(-x * s));
676 }
677
678 private static int nextPowerOfTwo(int value) {
679 return 1 << (32 - Integer.numberOfLeadingZeros(value));
680 }
681
682 private static FloatBuffer createNativeFloatBuffer(int size) {
683 ByteBuffer bb = ByteBuffer.allocateDirect(size * 4);
684 bb.order(ByteOrder.nativeOrder());
685 return bb.asFloatBuffer();
686 }
687
688 private static void logEglError(String func) {
689 Slog.e(TAG, func + " failed: error " + EGL14.eglGetError(), new Throwable());
690 }
691
692 private static boolean checkGlErrors(String func) {
693 return checkGlErrors(func, true);
694 }
695
696 private static boolean checkGlErrors(String func, boolean log) {
697 boolean hadError = false;
698 int error;
699 while ((error = GLES10.glGetError()) != GLES10.GL_NO_ERROR) {
700 if (log) {
701 Slog.e(TAG, func + " failed: error " + error, new Throwable());
702 }
703 hadError = true;
704 }
705 return hadError;
706 }
707
708 public void dump(PrintWriter pw) {
709 pw.println();
710 pw.println("Electron Beam State:");
711 pw.println(" mPrepared=" + mPrepared);
Jeff Brown8b9cf1c2012-10-07 14:54:17 -0700712 pw.println(" mMode=" + mMode);
Jeff Brown96307042012-07-27 15:51:34 -0700713 pw.println(" mDisplayLayerStack=" + mDisplayLayerStack);
714 pw.println(" mDisplayRotation=" + mDisplayRotation);
715 pw.println(" mDisplayWidth=" + mDisplayWidth);
716 pw.println(" mDisplayHeight=" + mDisplayHeight);
717 pw.println(" mSurfaceVisible=" + mSurfaceVisible);
Jeff Brown252c2062012-10-08 16:21:01 -0700718 pw.println(" mSurfaceAlpha=" + mSurfaceAlpha);
Jeff Brown96307042012-07-27 15:51:34 -0700719 }
720}