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.5f;
64    private static final float VSTRETCH_DURATION = 1.0f - HSTRETCH_DURATION;
65
66    // 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
71    // Set to true when the animation context has been fully prepared.
72    private boolean mPrepared;
73    private int mMode;
74
75    private final Display mDisplay;
76    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;
88    private float mSurfaceAlpha;
89
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
99    /**
100     * Animates an electron beam warming up.
101     */
102    public static final int MODE_WARM_UP = 0;
103
104    /**
105     * Animates an electron beam shutting off.
106     */
107    public static final int MODE_COOL_DOWN = 1;
108
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;
113
114    public ElectronBeam(Display display) {
115        mDisplay = display;
116    }
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     *
122     * @param mode The desired mode for the upcoming animation.
123     * @return True if the electron beam is ready, false if it is uncontrollable.
124     */
125    public boolean prepare(int mode) {
126        if (DEBUG) {
127            Slog.d(TAG, "prepare: mode=" + mode);
128        }
129
130        mMode = mode;
131
132        // Get the display size and adjust it for rotation.
133        mDisplay.getDisplayInfo(mDisplayInfo);
134        mDisplayLayerStack = mDisplay.getLayerStack();
135        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.
146        if (!tryPrepare()) {
147            dismiss();
148            return false;
149        }
150
151        // Done.
152        mPrepared = true;
153
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        }
166        return true;
167    }
168
169    private boolean tryPrepare() {
170        if (createSurface()) {
171            if (mMode == MODE_FADE) {
172                return true;
173            }
174            return createEglContext()
175                    && createEglSurface()
176                    && captureScreenshotTextureAndSetViewport();
177        }
178        return false;
179    }
180
181    /**
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();
195        destroySurface();
196        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
211        if (!mPrepared) {
212            return false;
213        }
214
215        if (mMode == MODE_FADE) {
216            return showSurface(1.0f - level);
217        }
218
219        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        }
241        return showSurface(1.0f);
242    }
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,
272                mMode == MODE_WARM_UP ? GLES10.GL_MODULATE : GLES10.GL_REPLACE);
273        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)
306        if (mMode == MODE_COOL_DOWN) {
307            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
527    private boolean createSurface() {
528        if (mSurfaceSession == null) {
529            mSurfaceSession = new SurfaceSession();
530        }
531
532        Surface.openTransaction();
533        try {
534            if (mSurface == null) {
535                try {
536                    int flags;
537                    if (mMode == MODE_FADE) {
538                        flags = Surface.FX_SURFACE_DIM | Surface.HIDDEN;
539                    } else {
540                        flags = Surface.OPAQUE | Surface.HIDDEN;
541                    }
542                    mSurface = new Surface(mSurfaceSession,
543                            "ElectronBeam", mDisplayWidth, mDisplayHeight,
544                            PixelFormat.OPAQUE, flags);
545                } catch (Surface.OutOfResourcesException ex) {
546                    Slog.e(TAG, "Unable to create surface.", ex);
547                    return false;
548                }
549            }
550
551            mSurface.setLayerStack(mDisplayLayerStack);
552            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        }
575        return true;
576    }
577
578    private boolean createEglSurface() {
579        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        }
600    }
601
602    private void destroySurface() {
603        if (mSurface != null) {
604            Surface.openTransaction();
605            try {
606                mSurface.destroy();
607            } finally {
608                Surface.closeTransaction();
609            }
610            mSurface = null;
611            mSurfaceVisible = false;
612            mSurfaceAlpha = 0f;
613        }
614    }
615
616    private boolean showSurface(float alpha) {
617        if (!mSurfaceVisible || mSurfaceAlpha != alpha) {
618            Surface.openTransaction();
619            try {
620                mSurface.setLayer(ELECTRON_BEAM_LAYER);
621                mSurface.setAlpha(alpha);
622                mSurface.show();
623            } finally {
624                Surface.closeTransaction();
625            }
626            mSurfaceVisible = true;
627            mSurfaceAlpha = alpha;
628        }
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);
712        pw.println("  mMode=" + mMode);
713        pw.println("  mDisplayLayerStack=" + mDisplayLayerStack);
714        pw.println("  mDisplayRotation=" + mDisplayRotation);
715        pw.println("  mDisplayWidth=" + mDisplayWidth);
716        pw.println("  mDisplayHeight=" + mDisplayHeight);
717        pw.println("  mSurfaceVisible=" + mSurfaceVisible);
718        pw.println("  mSurfaceAlpha=" + mSurfaceAlpha);
719    }
720}
721