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