ElectronBeam.java revision 252c206984299d7ce91c27536cafe1bb2fb9628d
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
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;
29import android.util.FloatMath;
30import android.util.Slog;
31import android.view.Display;
32import android.view.DisplayInfo;
33import android.view.Surface;
34import android.view.SurfaceSession;
35
36import java.io.PrintWriter;
37import java.nio.ByteBuffer;
38import java.nio.ByteOrder;
39import java.nio.FloatBuffer;
40
41/**
42 * Bzzzoooop!  *crackle*
43 * <p>
44 * Animates a screen transition from on to off or off to on by applying
45 * some GL transformations to a screenshot.
46 * </p><p>
47 * This component must only be created or accessed by the {@link Looper} thread
48 * that belongs to the {@link DisplayPowerController}.
49 * </p>
50 */
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.
63    private static final float HSTRETCH_DURATION = 0.4f;
64    private static final float VSTRETCH_DURATION = 1.0f - HSTRETCH_DURATION;
65
66    // Set to true when the animation context has been fully prepared.
67    private boolean mPrepared;
68    private int mMode;
69
70    private final Display mDisplay;
71    private final DisplayInfo mDisplayInfo = new DisplayInfo();
72    private int mDisplayLayerStack; // layer stack associated with primary display
73    private int mDisplayRotation;
74    private int mDisplayWidth;      // real width, not rotated
75    private int mDisplayHeight;     // real height, not rotated
76    private SurfaceSession mSurfaceSession;
77    private Surface mSurface;
78    private EGLDisplay mEglDisplay;
79    private EGLConfig mEglConfig;
80    private EGLContext mEglContext;
81    private EGLSurface mEglSurface;
82    private boolean mSurfaceVisible;
83    private float mSurfaceAlpha;
84
85    // Texture names.  We only use one texture, which contains the screenshot.
86    private final int[] mTexNames = new int[1];
87    private boolean mTexNamesGenerated;
88
89    // Vertex and corresponding texture coordinates.
90    // We have 4 2D vertices, so 8 elements.  The vertices form a quad.
91    private final FloatBuffer mVertexBuffer = createNativeFloatBuffer(8);
92    private final FloatBuffer mTexCoordBuffer = createNativeFloatBuffer(8);
93
94    /**
95     * Animates an electron beam warming up.
96     */
97    public static final int MODE_WARM_UP = 0;
98
99    /**
100     * Animates an electron beam shutting off.
101     */
102    public static final int MODE_COOL_DOWN = 1;
103
104    /**
105     * Animates a simple dim layer to fade the contents of the screen in or out progressively.
106     */
107    public static final int MODE_FADE = 2;
108
109    public ElectronBeam(Display display) {
110        mDisplay = display;
111    }
112
113    /**
114     * Warms up the electron beam in preparation for turning on or off.
115     * This method prepares a GL context, and captures a screen shot.
116     *
117     * @param mode The desired mode for the upcoming animation.
118     * @return True if the electron beam is ready, false if it is uncontrollable.
119     */
120    public boolean prepare(int mode) {
121        if (DEBUG) {
122            Slog.d(TAG, "prepare: mode=" + mode);
123        }
124
125        mMode = mode;
126
127        // Get the display size and adjust it for rotation.
128        mDisplay.getDisplayInfo(mDisplayInfo);
129        mDisplayLayerStack = mDisplay.getLayerStack();
130        mDisplayRotation = mDisplayInfo.rotation;
131        if (mDisplayRotation == Surface.ROTATION_90
132                || mDisplayRotation == Surface.ROTATION_270) {
133            mDisplayWidth = mDisplayInfo.logicalHeight;
134            mDisplayHeight = mDisplayInfo.logicalWidth;
135        } else {
136            mDisplayWidth = mDisplayInfo.logicalWidth;
137            mDisplayHeight = mDisplayInfo.logicalHeight;
138        }
139
140        // Prepare the surface for drawing.
141        if (!tryPrepare()) {
142            dismiss();
143            return false;
144        }
145
146        // Done.
147        mPrepared = true;
148        return true;
149    }
150
151    private boolean tryPrepare() {
152        if (createSurface()) {
153            if (mMode == MODE_FADE) {
154                return true;
155            }
156            return createEglContext()
157                    && createEglSurface()
158                    && captureScreenshotTextureAndSetViewport();
159        }
160        return false;
161    }
162
163    /**
164     * Dismisses the electron beam animation surface and cleans up.
165     *
166     * To prevent stray photons from leaking out after the electron beam has been
167     * turned off, it is a good idea to defer dismissing the animation until the
168     * electron beam has been turned back on fully.
169     */
170    public void dismiss() {
171        if (DEBUG) {
172            Slog.d(TAG, "dismiss");
173        }
174
175        destroyScreenshotTexture();
176        destroyEglSurface();
177        destroySurface();
178        mPrepared = false;
179    }
180
181    /**
182     * Draws an animation frame showing the electron beam activated at the
183     * specified level.
184     *
185     * @param level The electron beam level.
186     * @return True if successful.
187     */
188    public boolean draw(float level) {
189        if (DEBUG) {
190            Slog.d(TAG, "drawFrame: level=" + level);
191        }
192
193        if (!mPrepared) {
194            return false;
195        }
196
197        if (mMode == MODE_FADE) {
198            return showSurface(1.0f - level);
199        }
200
201        if (!attachEglContext()) {
202            return false;
203        }
204        try {
205            // Clear frame to solid black.
206            GLES10.glClearColor(0f, 0f, 0f, 1f);
207            GLES10.glClear(GLES10.GL_COLOR_BUFFER_BIT);
208
209            // Draw the frame.
210            if (level < HSTRETCH_DURATION) {
211                drawHStretch(1.0f - (level / HSTRETCH_DURATION));
212            } else {
213                drawVStretch(1.0f - ((level - HSTRETCH_DURATION) / VSTRETCH_DURATION));
214            }
215            if (checkGlErrors("drawFrame")) {
216                return false;
217            }
218
219            EGL14.eglSwapBuffers(mEglDisplay, mEglSurface);
220        } finally {
221            detachEglContext();
222        }
223        return showSurface(1.0f);
224    }
225
226    /**
227     * Draws a frame where the content of the electron beam is collapsing inwards upon
228     * itself vertically with red / green / blue channels dispersing and eventually
229     * merging down to a single horizontal line.
230     *
231     * @param stretch The stretch factor.  0.0 is no collapse, 1.0 is full collapse.
232     */
233    private void drawVStretch(float stretch) {
234        // compute interpolation scale factors for each color channel
235        final float ar = scurve(stretch, 7.5f);
236        final float ag = scurve(stretch, 8.0f);
237        final float ab = scurve(stretch, 8.5f);
238        if (DEBUG) {
239            Slog.d(TAG, "drawVStretch: stretch=" + stretch
240                    + ", ar=" + ar + ", ag=" + ag + ", ab=" + ab);
241        }
242
243        // set blending
244        GLES10.glBlendFunc(GLES10.GL_ONE, GLES10.GL_ONE);
245        GLES10.glEnable(GLES10.GL_BLEND);
246
247        // bind vertex buffer
248        GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer);
249        GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY);
250
251        // bind texture and set blending for drawing planes
252        GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, mTexNames[0]);
253        GLES10.glTexEnvx(GLES10.GL_TEXTURE_ENV, GLES10.GL_TEXTURE_ENV_MODE,
254                mMode == MODE_WARM_UP ? GLES10.GL_MODULATE : GLES10.GL_REPLACE);
255        GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
256                GLES10.GL_TEXTURE_MAG_FILTER, GLES10.GL_LINEAR);
257        GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
258                GLES10.GL_TEXTURE_MIN_FILTER, GLES10.GL_LINEAR);
259        GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
260                GLES10.GL_TEXTURE_WRAP_S, GLES10.GL_CLAMP_TO_EDGE);
261        GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
262                GLES10.GL_TEXTURE_WRAP_T, GLES10.GL_CLAMP_TO_EDGE);
263        GLES10.glEnable(GLES10.GL_TEXTURE_2D);
264        GLES10.glTexCoordPointer(2, GLES10.GL_FLOAT, 0, mTexCoordBuffer);
265        GLES10.glEnableClientState(GLES10.GL_TEXTURE_COORD_ARRAY);
266
267        // draw the red plane
268        setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ar);
269        GLES10.glColorMask(true, false, false, true);
270        GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
271
272        // draw the green plane
273        setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag);
274        GLES10.glColorMask(false, true, false, true);
275        GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
276
277        // draw the blue plane
278        setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ab);
279        GLES10.glColorMask(false, false, true, true);
280        GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
281
282        // clean up after drawing planes
283        GLES10.glDisable(GLES10.GL_TEXTURE_2D);
284        GLES10.glDisableClientState(GLES10.GL_TEXTURE_COORD_ARRAY);
285        GLES10.glColorMask(true, true, true, true);
286
287        // draw the white highlight (we use the last vertices)
288        if (mMode == MODE_COOL_DOWN) {
289            GLES10.glColor4f(ag, ag, ag, 1.0f);
290            GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
291        }
292
293        // clean up
294        GLES10.glDisableClientState(GLES10.GL_VERTEX_ARRAY);
295        GLES10.glDisable(GLES10.GL_BLEND);
296    }
297
298    /**
299     * Draws a frame where the electron beam has been stretched out into
300     * a thin white horizontal line that fades as it expands outwards.
301     *
302     * @param stretch The stretch factor.  0.0 is no stretch / no fade,
303     * 1.0 is maximum stretch / maximum fade.
304     */
305    private void drawHStretch(float stretch) {
306        // compute interpolation scale factor
307        final float ag = scurve(stretch, 8.0f);
308        if (DEBUG) {
309            Slog.d(TAG, "drawHStretch: stretch=" + stretch + ", ag=" + ag);
310        }
311
312        if (stretch < 1.0f) {
313            // bind vertex buffer
314            GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer);
315            GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY);
316
317            // draw narrow fading white line
318            setHStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag);
319            GLES10.glColor4f(1.0f - ag, 1.0f - ag, 1.0f - ag, 1.0f);
320            GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
321
322            // clean up
323            GLES10.glDisableClientState(GLES10.GL_VERTEX_ARRAY);
324        }
325    }
326
327    private static void setVStretchQuad(FloatBuffer vtx, float dw, float dh, float a) {
328        final float w = dw + (dw * a);
329        final float h = dh - (dh * a);
330        final float x = (dw - w) * 0.5f;
331        final float y = (dh - h) * 0.5f;
332        setQuad(vtx, x, y, w, h);
333    }
334
335    private static void setHStretchQuad(FloatBuffer vtx, float dw, float dh, float a) {
336        final float w = dw + (dw * a);
337        final float h = 1.0f;
338        final float x = (dw - w) * 0.5f;
339        final float y = (dh - h) * 0.5f;
340        setQuad(vtx, x, y, w, h);
341    }
342
343    private static void setQuad(FloatBuffer vtx, float x, float y, float w, float h) {
344        if (DEBUG) {
345            Slog.d(TAG, "setQuad: x=" + x + ", y=" + y + ", w=" + w + ", h=" + h);
346        }
347        vtx.put(0, x);
348        vtx.put(1, y);
349        vtx.put(2, x);
350        vtx.put(3, y + h);
351        vtx.put(4, x + w);
352        vtx.put(5, y + h);
353        vtx.put(6, x + w);
354        vtx.put(7, y);
355    }
356
357    private boolean captureScreenshotTextureAndSetViewport() {
358        // TODO: Use a SurfaceTexture to avoid the extra texture upload.
359        Bitmap bitmap = Surface.screenshot(mDisplayWidth, mDisplayHeight,
360                0, ELECTRON_BEAM_LAYER - 1);
361        if (bitmap == null) {
362            Slog.e(TAG, "Could not take a screenshot!");
363            return false;
364        }
365        try {
366            if (!attachEglContext()) {
367                return false;
368            }
369            try {
370                if (!mTexNamesGenerated) {
371                    GLES10.glGenTextures(1, mTexNames, 0);
372                    if (checkGlErrors("glGenTextures")) {
373                        return false;
374                    }
375                    mTexNamesGenerated = true;
376                }
377
378                GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, mTexNames[0]);
379                if (checkGlErrors("glBindTexture")) {
380                    return false;
381                }
382
383                float u = 1.0f;
384                float v = 1.0f;
385                GLUtils.texImage2D(GLES10.GL_TEXTURE_2D, 0, bitmap, 0);
386                if (checkGlErrors("glTexImage2D, first try", false)) {
387                    // Try a power of two size texture instead.
388                    int tw = nextPowerOfTwo(mDisplayWidth);
389                    int th = nextPowerOfTwo(mDisplayHeight);
390                    int format = GLUtils.getInternalFormat(bitmap);
391                    GLES10.glTexImage2D(GLES10.GL_TEXTURE_2D, 0,
392                            format, tw, th, 0,
393                            format, GLES10.GL_UNSIGNED_BYTE, null);
394                    if (checkGlErrors("glTexImage2D, second try")) {
395                        return false;
396                    }
397
398                    GLUtils.texSubImage2D(GLES10.GL_TEXTURE_2D, 0, 0, 0, bitmap);
399                    if (checkGlErrors("glTexSubImage2D")) {
400                        return false;
401                    }
402
403                    u = (float)mDisplayWidth / tw;
404                    v = (float)mDisplayHeight / th;
405                }
406
407                // Set up texture coordinates for a quad.
408                // We might need to change this if the texture ends up being
409                // a different size from the display for some reason.
410                mTexCoordBuffer.put(0, 0f);
411                mTexCoordBuffer.put(1, v);
412                mTexCoordBuffer.put(2, 0f);
413                mTexCoordBuffer.put(3, 0f);
414                mTexCoordBuffer.put(4, u);
415                mTexCoordBuffer.put(5, 0f);
416                mTexCoordBuffer.put(6, u);
417                mTexCoordBuffer.put(7, v);
418
419                // Set up our viewport.
420                GLES10.glViewport(0, 0, mDisplayWidth, mDisplayHeight);
421                GLES10.glMatrixMode(GLES10.GL_PROJECTION);
422                GLES10.glLoadIdentity();
423                GLES10.glOrthof(0, mDisplayWidth, 0, mDisplayHeight, 0, 1);
424                GLES10.glMatrixMode(GLES10.GL_MODELVIEW);
425                GLES10.glLoadIdentity();
426                GLES10.glMatrixMode(GLES10.GL_TEXTURE);
427                GLES10.glLoadIdentity();
428            } finally {
429                detachEglContext();
430            }
431        } finally {
432            bitmap.recycle();
433        }
434        return true;
435    }
436
437    private void destroyScreenshotTexture() {
438        if (mTexNamesGenerated) {
439            mTexNamesGenerated = false;
440            if (attachEglContext()) {
441                try {
442                    GLES10.glDeleteTextures(1, mTexNames, 0);
443                    checkGlErrors("glDeleteTextures");
444                } finally {
445                    detachEglContext();
446                }
447            }
448        }
449    }
450
451    private boolean createEglContext() {
452        if (mEglDisplay == null) {
453            mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
454            if (mEglDisplay == EGL14.EGL_NO_DISPLAY) {
455                logEglError("eglGetDisplay");
456                return false;
457            }
458
459            int[] version = new int[2];
460            if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) {
461                mEglDisplay = null;
462                logEglError("eglInitialize");
463                return false;
464            }
465        }
466
467        if (mEglConfig == null) {
468            int[] eglConfigAttribList = new int[] {
469                    EGL14.EGL_RED_SIZE, 8,
470                    EGL14.EGL_GREEN_SIZE, 8,
471                    EGL14.EGL_BLUE_SIZE, 8,
472                    EGL14.EGL_ALPHA_SIZE, 8,
473                    EGL14.EGL_NONE
474            };
475            int[] numEglConfigs = new int[1];
476            EGLConfig[] eglConfigs = new EGLConfig[1];
477            if (!EGL14.eglChooseConfig(mEglDisplay, eglConfigAttribList, 0,
478                    eglConfigs, 0, eglConfigs.length, numEglConfigs, 0)) {
479                logEglError("eglChooseConfig");
480                return false;
481            }
482            mEglConfig = eglConfigs[0];
483        }
484
485        if (mEglContext == null) {
486            int[] eglContextAttribList = new int[] {
487                    EGL14.EGL_NONE
488            };
489            mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig,
490                    EGL14.EGL_NO_CONTEXT, eglContextAttribList, 0);
491            if (mEglContext == null) {
492                logEglError("eglCreateContext");
493                return false;
494            }
495        }
496        return true;
497    }
498
499    /* not used because it is too expensive to create / destroy contexts all of the time
500    private void destroyEglContext() {
501        if (mEglContext != null) {
502            if (!EGL14.eglDestroyContext(mEglDisplay, mEglContext)) {
503                logEglError("eglDestroyContext");
504            }
505            mEglContext = null;
506        }
507    }*/
508
509    private boolean createSurface() {
510        if (mSurfaceSession == null) {
511            mSurfaceSession = new SurfaceSession();
512        }
513
514        Surface.openTransaction();
515        try {
516            if (mSurface == null) {
517                try {
518                    int flags;
519                    if (mMode == MODE_FADE) {
520                        flags = Surface.FX_SURFACE_DIM | Surface.HIDDEN;
521                    } else {
522                        flags = Surface.OPAQUE | Surface.HIDDEN;
523                    }
524                    mSurface = new Surface(mSurfaceSession,
525                            "ElectronBeam", mDisplayWidth, mDisplayHeight,
526                            PixelFormat.OPAQUE, flags);
527                } catch (Surface.OutOfResourcesException ex) {
528                    Slog.e(TAG, "Unable to create surface.", ex);
529                    return false;
530                }
531            }
532
533            mSurface.setLayerStack(mDisplayLayerStack);
534            mSurface.setSize(mDisplayWidth, mDisplayHeight);
535
536            switch (mDisplayRotation) {
537                case Surface.ROTATION_0:
538                    mSurface.setPosition(0, 0);
539                    mSurface.setMatrix(1, 0, 0, 1);
540                    break;
541                case Surface.ROTATION_90:
542                    mSurface.setPosition(0, mDisplayWidth);
543                    mSurface.setMatrix(0, -1, 1, 0);
544                    break;
545                case Surface.ROTATION_180:
546                    mSurface.setPosition(mDisplayWidth, mDisplayHeight);
547                    mSurface.setMatrix(-1, 0, 0, -1);
548                    break;
549                case Surface.ROTATION_270:
550                    mSurface.setPosition(mDisplayHeight, 0);
551                    mSurface.setMatrix(0, 1, -1, 0);
552                    break;
553            }
554        } finally {
555            Surface.closeTransaction();
556        }
557        return true;
558    }
559
560    private boolean createEglSurface() {
561        if (mEglSurface == null) {
562            int[] eglSurfaceAttribList = new int[] {
563                    EGL14.EGL_NONE
564            };
565            mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface,
566                    eglSurfaceAttribList, 0);
567            if (mEglSurface == null) {
568                logEglError("eglCreateWindowSurface");
569                return false;
570            }
571        }
572        return true;
573    }
574
575    private void destroyEglSurface() {
576        if (mEglSurface != null) {
577            if (!EGL14.eglDestroySurface(mEglDisplay, mEglSurface)) {
578                logEglError("eglDestroySurface");
579            }
580            mEglSurface = null;
581        }
582    }
583
584    private void destroySurface() {
585        if (mSurface != 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
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
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
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     */
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
656    private static float sigmoid(float x, float s) {
657        return 1.0f / (1.0f + FloatMath.exp(-x * s));
658    }
659
660    private static int nextPowerOfTwo(int value) {
661        return 1 << (32 - Integer.numberOfLeadingZeros(value));
662    }
663
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
670    private static void logEglError(String func) {
671        Slog.e(TAG, func + " failed: error " + EGL14.eglGetError(), new Throwable());
672    }
673
674    private static boolean checkGlErrors(String func) {
675        return checkGlErrors(func, true);
676    }
677
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
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("  mDisplayRotation=" + mDisplayRotation);
697        pw.println("  mDisplayWidth=" + mDisplayWidth);
698        pw.println("  mDisplayHeight=" + mDisplayHeight);
699        pw.println("  mSurfaceVisible=" + mSurfaceVisible);
700        pw.println("  mSurfaceAlpha=" + mSurfaceAlpha);
701    }
702}
703