summaryrefslogtreecommitdiffstats
path: root/services/core/java/com/android/server/power/ElectronBeam.java
diff options
context:
space:
mode:
Diffstat (limited to 'services/core/java/com/android/server/power/ElectronBeam.java')
-rw-r--r--services/core/java/com/android/server/power/ElectronBeam.java733
1 files changed, 733 insertions, 0 deletions
diff --git a/services/core/java/com/android/server/power/ElectronBeam.java b/services/core/java/com/android/server/power/ElectronBeam.java
new file mode 100644
index 0000000..729bd16
--- /dev/null
+++ b/services/core/java/com/android/server/power/ElectronBeam.java
@@ -0,0 +1,733 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import java.io.PrintWriter;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import android.graphics.PixelFormat;
+import android.graphics.SurfaceTexture;
+import android.opengl.EGL14;
+import android.opengl.EGLConfig;
+import android.opengl.EGLContext;
+import android.opengl.EGLDisplay;
+import android.opengl.EGLSurface;
+import android.opengl.GLES10;
+import android.opengl.GLES11Ext;
+import android.os.Looper;
+import android.util.FloatMath;
+import android.util.Slog;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.Surface.OutOfResourcesException;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+
+import com.android.server.display.DisplayManagerService;
+import com.android.server.display.DisplayTransactionListener;
+
+/**
+ * Bzzzoooop! *crackle*
+ * <p>
+ * Animates a screen transition from on to off or off to on by applying
+ * some GL transformations to a screenshot.
+ * </p><p>
+ * This component must only be created or accessed by the {@link Looper} thread
+ * that belongs to the {@link DisplayPowerController}.
+ * </p>
+ */
+final class ElectronBeam {
+ private static final String TAG = "ElectronBeam";
+
+ private static final boolean DEBUG = false;
+
+ // The layer for the electron beam surface.
+ // This is currently hardcoded to be one layer above the boot animation.
+ private static final int ELECTRON_BEAM_LAYER = 0x40000001;
+
+ // The relative proportion of the animation to spend performing
+ // the horizontal stretch effect. The remainder is spent performing
+ // the vertical stretch effect.
+ private static final float HSTRETCH_DURATION = 0.5f;
+ private static final float VSTRETCH_DURATION = 1.0f - HSTRETCH_DURATION;
+
+ // The number of frames to draw when preparing the animation so that it will
+ // be ready to run smoothly. We use 3 frames because we are triple-buffered.
+ // See code for details.
+ private static final int DEJANK_FRAMES = 3;
+
+ // Set to true when the animation context has been fully prepared.
+ private boolean mPrepared;
+ private int mMode;
+
+ private final DisplayManagerService mDisplayManager;
+ private int mDisplayLayerStack; // layer stack associated with primary display
+ private int mDisplayWidth; // real width, not rotated
+ private int mDisplayHeight; // real height, not rotated
+ private SurfaceSession mSurfaceSession;
+ private SurfaceControl mSurfaceControl;
+ private Surface mSurface;
+ private NaturalSurfaceLayout mSurfaceLayout;
+ private EGLDisplay mEglDisplay;
+ private EGLConfig mEglConfig;
+ private EGLContext mEglContext;
+ private EGLSurface mEglSurface;
+ private boolean mSurfaceVisible;
+ private float mSurfaceAlpha;
+
+ // Texture names. We only use one texture, which contains the screenshot.
+ private final int[] mTexNames = new int[1];
+ private boolean mTexNamesGenerated;
+ private final float mTexMatrix[] = new float[16];
+
+ // Vertex and corresponding texture coordinates.
+ // We have 4 2D vertices, so 8 elements. The vertices form a quad.
+ private final FloatBuffer mVertexBuffer = createNativeFloatBuffer(8);
+ private final FloatBuffer mTexCoordBuffer = createNativeFloatBuffer(8);
+
+ /**
+ * Animates an electron beam warming up.
+ */
+ public static final int MODE_WARM_UP = 0;
+
+ /**
+ * Animates an electron beam shutting off.
+ */
+ public static final int MODE_COOL_DOWN = 1;
+
+ /**
+ * Animates a simple dim layer to fade the contents of the screen in or out progressively.
+ */
+ public static final int MODE_FADE = 2;
+
+
+ public ElectronBeam(DisplayManagerService displayManager) {
+ mDisplayManager = displayManager;
+ }
+
+ /**
+ * Warms up the electron beam in preparation for turning on or off.
+ * This method prepares a GL context, and captures a screen shot.
+ *
+ * @param mode The desired mode for the upcoming animation.
+ * @return True if the electron beam is ready, false if it is uncontrollable.
+ */
+ public boolean prepare(int mode) {
+ if (DEBUG) {
+ Slog.d(TAG, "prepare: mode=" + mode);
+ }
+
+ mMode = mode;
+
+ // Get the display size and layer stack.
+ // This is not expected to change while the electron beam surface is showing.
+ DisplayInfo displayInfo = mDisplayManager.getDisplayInfo(Display.DEFAULT_DISPLAY);
+ mDisplayLayerStack = displayInfo.layerStack;
+ mDisplayWidth = displayInfo.getNaturalWidth();
+ mDisplayHeight = displayInfo.getNaturalHeight();
+
+ // Prepare the surface for drawing.
+ if (!tryPrepare()) {
+ dismiss();
+ return false;
+ }
+
+ // Done.
+ mPrepared = true;
+
+ // Dejanking optimization.
+ // Some GL drivers can introduce a lot of lag in the first few frames as they
+ // initialize their state and allocate graphics buffers for rendering.
+ // Work around this problem by rendering the first frame of the animation a few
+ // times. The rest of the animation should run smoothly thereafter.
+ // The frames we draw here aren't visible because we are essentially just
+ // painting the screenshot as-is.
+ if (mode == MODE_COOL_DOWN) {
+ for (int i = 0; i < DEJANK_FRAMES; i++) {
+ draw(1.0f);
+ }
+ }
+ return true;
+ }
+
+ private boolean tryPrepare() {
+ if (createSurface()) {
+ if (mMode == MODE_FADE) {
+ return true;
+ }
+ return createEglContext()
+ && createEglSurface()
+ && captureScreenshotTextureAndSetViewport();
+ }
+ return false;
+ }
+
+ /**
+ * Dismisses the electron beam animation surface and cleans up.
+ *
+ * To prevent stray photons from leaking out after the electron beam has been
+ * turned off, it is a good idea to defer dismissing the animation until the
+ * electron beam has been turned back on fully.
+ */
+ public void dismiss() {
+ if (DEBUG) {
+ Slog.d(TAG, "dismiss");
+ }
+
+ destroyScreenshotTexture();
+ destroyEglSurface();
+ destroySurface();
+ mPrepared = false;
+ }
+
+ /**
+ * Draws an animation frame showing the electron beam activated at the
+ * specified level.
+ *
+ * @param level The electron beam level.
+ * @return True if successful.
+ */
+ public boolean draw(float level) {
+ if (DEBUG) {
+ Slog.d(TAG, "drawFrame: level=" + level);
+ }
+
+ if (!mPrepared) {
+ return false;
+ }
+
+ if (mMode == MODE_FADE) {
+ return showSurface(1.0f - level);
+ }
+
+ if (!attachEglContext()) {
+ return false;
+ }
+ try {
+ // Clear frame to solid black.
+ GLES10.glClearColor(0f, 0f, 0f, 1f);
+ GLES10.glClear(GLES10.GL_COLOR_BUFFER_BIT);
+
+ // Draw the frame.
+ if (level < HSTRETCH_DURATION) {
+ drawHStretch(1.0f - (level / HSTRETCH_DURATION));
+ } else {
+ drawVStretch(1.0f - ((level - HSTRETCH_DURATION) / VSTRETCH_DURATION));
+ }
+ if (checkGlErrors("drawFrame")) {
+ return false;
+ }
+
+ EGL14.eglSwapBuffers(mEglDisplay, mEglSurface);
+ } finally {
+ detachEglContext();
+ }
+ return showSurface(1.0f);
+ }
+
+ /**
+ * Draws a frame where the content of the electron beam is collapsing inwards upon
+ * itself vertically with red / green / blue channels dispersing and eventually
+ * merging down to a single horizontal line.
+ *
+ * @param stretch The stretch factor. 0.0 is no collapse, 1.0 is full collapse.
+ */
+ private void drawVStretch(float stretch) {
+ // compute interpolation scale factors for each color channel
+ final float ar = scurve(stretch, 7.5f);
+ final float ag = scurve(stretch, 8.0f);
+ final float ab = scurve(stretch, 8.5f);
+ if (DEBUG) {
+ Slog.d(TAG, "drawVStretch: stretch=" + stretch
+ + ", ar=" + ar + ", ag=" + ag + ", ab=" + ab);
+ }
+
+ // set blending
+ GLES10.glBlendFunc(GLES10.GL_ONE, GLES10.GL_ONE);
+ GLES10.glEnable(GLES10.GL_BLEND);
+
+ // bind vertex buffer
+ GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer);
+ GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY);
+
+ // set-up texturing
+ GLES10.glDisable(GLES10.GL_TEXTURE_2D);
+ GLES10.glEnable(GLES11Ext.GL_TEXTURE_EXTERNAL_OES);
+
+ // bind texture and set blending for drawing planes
+ GLES10.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTexNames[0]);
+ GLES10.glTexEnvx(GLES10.GL_TEXTURE_ENV, GLES10.GL_TEXTURE_ENV_MODE,
+ mMode == MODE_WARM_UP ? GLES10.GL_MODULATE : GLES10.GL_REPLACE);
+ GLES10.glTexParameterx(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
+ GLES10.GL_TEXTURE_MAG_FILTER, GLES10.GL_LINEAR);
+ GLES10.glTexParameterx(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
+ GLES10.GL_TEXTURE_MIN_FILTER, GLES10.GL_LINEAR);
+ GLES10.glTexParameterx(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
+ GLES10.GL_TEXTURE_WRAP_S, GLES10.GL_CLAMP_TO_EDGE);
+ GLES10.glTexParameterx(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
+ GLES10.GL_TEXTURE_WRAP_T, GLES10.GL_CLAMP_TO_EDGE);
+ GLES10.glEnable(GLES11Ext.GL_TEXTURE_EXTERNAL_OES);
+ GLES10.glTexCoordPointer(2, GLES10.GL_FLOAT, 0, mTexCoordBuffer);
+ GLES10.glEnableClientState(GLES10.GL_TEXTURE_COORD_ARRAY);
+
+ // draw the red plane
+ setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ar);
+ GLES10.glColorMask(true, false, false, true);
+ GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
+
+ // draw the green plane
+ setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag);
+ GLES10.glColorMask(false, true, false, true);
+ GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
+
+ // draw the blue plane
+ setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ab);
+ GLES10.glColorMask(false, false, true, true);
+ GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
+
+ // clean up after drawing planes
+ GLES10.glDisable(GLES11Ext.GL_TEXTURE_EXTERNAL_OES);
+ GLES10.glDisableClientState(GLES10.GL_TEXTURE_COORD_ARRAY);
+ GLES10.glColorMask(true, true, true, true);
+
+ // draw the white highlight (we use the last vertices)
+ if (mMode == MODE_COOL_DOWN) {
+ GLES10.glColor4f(ag, ag, ag, 1.0f);
+ GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
+ }
+
+ // clean up
+ GLES10.glDisableClientState(GLES10.GL_VERTEX_ARRAY);
+ GLES10.glDisable(GLES10.GL_BLEND);
+ }
+
+ /**
+ * Draws a frame where the electron beam has been stretched out into
+ * a thin white horizontal line that fades as it collapses inwards.
+ *
+ * @param stretch The stretch factor. 0.0 is maximum stretch / no fade,
+ * 1.0 is collapsed / maximum fade.
+ */
+ private void drawHStretch(float stretch) {
+ // compute interpolation scale factor
+ final float ag = scurve(stretch, 8.0f);
+ if (DEBUG) {
+ Slog.d(TAG, "drawHStretch: stretch=" + stretch + ", ag=" + ag);
+ }
+
+ if (stretch < 1.0f) {
+ // bind vertex buffer
+ GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer);
+ GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY);
+
+ // draw narrow fading white line
+ setHStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag);
+ GLES10.glColor4f(1.0f - ag*0.75f, 1.0f - ag*0.75f, 1.0f - ag*0.75f, 1.0f);
+ GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
+
+ // clean up
+ GLES10.glDisableClientState(GLES10.GL_VERTEX_ARRAY);
+ }
+ }
+
+ private static void setVStretchQuad(FloatBuffer vtx, float dw, float dh, float a) {
+ final float w = dw + (dw * a);
+ final float h = dh - (dh * a);
+ final float x = (dw - w) * 0.5f;
+ final float y = (dh - h) * 0.5f;
+ setQuad(vtx, x, y, w, h);
+ }
+
+ private static void setHStretchQuad(FloatBuffer vtx, float dw, float dh, float a) {
+ final float w = 2 * dw * (1.0f - a);
+ final float h = 1.0f;
+ final float x = (dw - w) * 0.5f;
+ final float y = (dh - h) * 0.5f;
+ setQuad(vtx, x, y, w, h);
+ }
+
+ private static void setQuad(FloatBuffer vtx, float x, float y, float w, float h) {
+ if (DEBUG) {
+ Slog.d(TAG, "setQuad: x=" + x + ", y=" + y + ", w=" + w + ", h=" + h);
+ }
+ vtx.put(0, x);
+ vtx.put(1, y);
+ vtx.put(2, x);
+ vtx.put(3, y + h);
+ vtx.put(4, x + w);
+ vtx.put(5, y + h);
+ vtx.put(6, x + w);
+ vtx.put(7, y);
+ }
+
+ private boolean captureScreenshotTextureAndSetViewport() {
+ if (!attachEglContext()) {
+ return false;
+ }
+ try {
+ if (!mTexNamesGenerated) {
+ GLES10.glGenTextures(1, mTexNames, 0);
+ if (checkGlErrors("glGenTextures")) {
+ return false;
+ }
+ mTexNamesGenerated = true;
+ }
+
+ final SurfaceTexture st = new SurfaceTexture(mTexNames[0]);
+ final Surface s = new Surface(st);
+ try {
+ SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay(
+ SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN), s);
+ } finally {
+ s.release();
+ }
+
+ st.updateTexImage();
+ st.getTransformMatrix(mTexMatrix);
+
+ // Set up texture coordinates for a quad.
+ // We might need to change this if the texture ends up being
+ // a different size from the display for some reason.
+ mTexCoordBuffer.put(0, 0f); mTexCoordBuffer.put(1, 0f);
+ mTexCoordBuffer.put(2, 0f); mTexCoordBuffer.put(3, 1f);
+ mTexCoordBuffer.put(4, 1f); mTexCoordBuffer.put(5, 1f);
+ mTexCoordBuffer.put(6, 1f); mTexCoordBuffer.put(7, 0f);
+
+ // Set up our viewport.
+ GLES10.glViewport(0, 0, mDisplayWidth, mDisplayHeight);
+ GLES10.glMatrixMode(GLES10.GL_PROJECTION);
+ GLES10.glLoadIdentity();
+ GLES10.glOrthof(0, mDisplayWidth, 0, mDisplayHeight, 0, 1);
+ GLES10.glMatrixMode(GLES10.GL_MODELVIEW);
+ GLES10.glLoadIdentity();
+ GLES10.glMatrixMode(GLES10.GL_TEXTURE);
+ GLES10.glLoadIdentity();
+ GLES10.glLoadMatrixf(mTexMatrix, 0);
+ } finally {
+ detachEglContext();
+ }
+ return true;
+ }
+
+ private void destroyScreenshotTexture() {
+ if (mTexNamesGenerated) {
+ mTexNamesGenerated = false;
+ if (attachEglContext()) {
+ try {
+ GLES10.glDeleteTextures(1, mTexNames, 0);
+ checkGlErrors("glDeleteTextures");
+ } finally {
+ detachEglContext();
+ }
+ }
+ }
+ }
+
+ private boolean createEglContext() {
+ if (mEglDisplay == null) {
+ mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
+ if (mEglDisplay == EGL14.EGL_NO_DISPLAY) {
+ logEglError("eglGetDisplay");
+ return false;
+ }
+
+ int[] version = new int[2];
+ if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) {
+ mEglDisplay = null;
+ logEglError("eglInitialize");
+ return false;
+ }
+ }
+
+ if (mEglConfig == null) {
+ int[] eglConfigAttribList = new int[] {
+ EGL14.EGL_RED_SIZE, 8,
+ EGL14.EGL_GREEN_SIZE, 8,
+ EGL14.EGL_BLUE_SIZE, 8,
+ EGL14.EGL_ALPHA_SIZE, 8,
+ EGL14.EGL_NONE
+ };
+ int[] numEglConfigs = new int[1];
+ EGLConfig[] eglConfigs = new EGLConfig[1];
+ if (!EGL14.eglChooseConfig(mEglDisplay, eglConfigAttribList, 0,
+ eglConfigs, 0, eglConfigs.length, numEglConfigs, 0)) {
+ logEglError("eglChooseConfig");
+ return false;
+ }
+ mEglConfig = eglConfigs[0];
+ }
+
+ if (mEglContext == null) {
+ int[] eglContextAttribList = new int[] {
+ EGL14.EGL_NONE
+ };
+ mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig,
+ EGL14.EGL_NO_CONTEXT, eglContextAttribList, 0);
+ if (mEglContext == null) {
+ logEglError("eglCreateContext");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /* not used because it is too expensive to create / destroy contexts all of the time
+ private void destroyEglContext() {
+ if (mEglContext != null) {
+ if (!EGL14.eglDestroyContext(mEglDisplay, mEglContext)) {
+ logEglError("eglDestroyContext");
+ }
+ mEglContext = null;
+ }
+ }*/
+
+ private boolean createSurface() {
+ if (mSurfaceSession == null) {
+ mSurfaceSession = new SurfaceSession();
+ }
+
+ SurfaceControl.openTransaction();
+ try {
+ if (mSurfaceControl == null) {
+ try {
+ int flags;
+ if (mMode == MODE_FADE) {
+ flags = SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN;
+ } else {
+ flags = SurfaceControl.OPAQUE | SurfaceControl.HIDDEN;
+ }
+ mSurfaceControl = new SurfaceControl(mSurfaceSession,
+ "ElectronBeam", mDisplayWidth, mDisplayHeight,
+ PixelFormat.OPAQUE, flags);
+ } catch (OutOfResourcesException ex) {
+ Slog.e(TAG, "Unable to create surface.", ex);
+ return false;
+ }
+ }
+
+ mSurfaceControl.setLayerStack(mDisplayLayerStack);
+ mSurfaceControl.setSize(mDisplayWidth, mDisplayHeight);
+ mSurface = new Surface();
+ mSurface.copyFrom(mSurfaceControl);
+
+ mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManager, mSurfaceControl);
+ mSurfaceLayout.onDisplayTransaction();
+ } finally {
+ SurfaceControl.closeTransaction();
+ }
+ return true;
+ }
+
+ private boolean createEglSurface() {
+ if (mEglSurface == null) {
+ int[] eglSurfaceAttribList = new int[] {
+ EGL14.EGL_NONE
+ };
+ // turn our SurfaceControl into a Surface
+ mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface,
+ eglSurfaceAttribList, 0);
+ if (mEglSurface == null) {
+ logEglError("eglCreateWindowSurface");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void destroyEglSurface() {
+ if (mEglSurface != null) {
+ if (!EGL14.eglDestroySurface(mEglDisplay, mEglSurface)) {
+ logEglError("eglDestroySurface");
+ }
+ mEglSurface = null;
+ }
+ }
+
+ private void destroySurface() {
+ if (mSurfaceControl != null) {
+ mSurfaceLayout.dispose();
+ mSurfaceLayout = null;
+ SurfaceControl.openTransaction();
+ try {
+ mSurfaceControl.destroy();
+ mSurface.release();
+ } finally {
+ SurfaceControl.closeTransaction();
+ }
+ mSurfaceControl = null;
+ mSurfaceVisible = false;
+ mSurfaceAlpha = 0f;
+ }
+ }
+
+ private boolean showSurface(float alpha) {
+ if (!mSurfaceVisible || mSurfaceAlpha != alpha) {
+ SurfaceControl.openTransaction();
+ try {
+ mSurfaceControl.setLayer(ELECTRON_BEAM_LAYER);
+ mSurfaceControl.setAlpha(alpha);
+ mSurfaceControl.show();
+ } finally {
+ SurfaceControl.closeTransaction();
+ }
+ mSurfaceVisible = true;
+ mSurfaceAlpha = alpha;
+ }
+ return true;
+ }
+
+ private boolean attachEglContext() {
+ if (mEglSurface == null) {
+ return false;
+ }
+ if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ logEglError("eglMakeCurrent");
+ return false;
+ }
+ return true;
+ }
+
+ private void detachEglContext() {
+ if (mEglDisplay != null) {
+ EGL14.eglMakeCurrent(mEglDisplay,
+ EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
+ }
+ }
+
+ /**
+ * Interpolates a value in the range 0 .. 1 along a sigmoid curve
+ * yielding a result in the range 0 .. 1 scaled such that:
+ * scurve(0) == 0, scurve(0.5) == 0.5, scurve(1) == 1.
+ */
+ private static float scurve(float value, float s) {
+ // A basic sigmoid has the form y = 1.0f / FloatMap.exp(-x * s).
+ // Here we take the input datum and shift it by 0.5 so that the
+ // domain spans the range -0.5 .. 0.5 instead of 0 .. 1.
+ final float x = value - 0.5f;
+
+ // Next apply the sigmoid function to the scaled value
+ // which produces a value in the range 0 .. 1 so we subtract
+ // 0.5 to get a value in the range -0.5 .. 0.5 instead.
+ final float y = sigmoid(x, s) - 0.5f;
+
+ // To obtain the desired boundary conditions we need to scale
+ // the result so that it fills a range of -1 .. 1.
+ final float v = sigmoid(0.5f, s) - 0.5f;
+
+ // And finally remap the value back to a range of 0 .. 1.
+ return y / v * 0.5f + 0.5f;
+ }
+
+ private static float sigmoid(float x, float s) {
+ return 1.0f / (1.0f + FloatMath.exp(-x * s));
+ }
+
+ private static FloatBuffer createNativeFloatBuffer(int size) {
+ ByteBuffer bb = ByteBuffer.allocateDirect(size * 4);
+ bb.order(ByteOrder.nativeOrder());
+ return bb.asFloatBuffer();
+ }
+
+ private static void logEglError(String func) {
+ Slog.e(TAG, func + " failed: error " + EGL14.eglGetError(), new Throwable());
+ }
+
+ private static boolean checkGlErrors(String func) {
+ return checkGlErrors(func, true);
+ }
+
+ private static boolean checkGlErrors(String func, boolean log) {
+ boolean hadError = false;
+ int error;
+ while ((error = GLES10.glGetError()) != GLES10.GL_NO_ERROR) {
+ if (log) {
+ Slog.e(TAG, func + " failed: error " + error, new Throwable());
+ }
+ hadError = true;
+ }
+ return hadError;
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println();
+ pw.println("Electron Beam State:");
+ pw.println(" mPrepared=" + mPrepared);
+ pw.println(" mMode=" + mMode);
+ pw.println(" mDisplayLayerStack=" + mDisplayLayerStack);
+ pw.println(" mDisplayWidth=" + mDisplayWidth);
+ pw.println(" mDisplayHeight=" + mDisplayHeight);
+ pw.println(" mSurfaceVisible=" + mSurfaceVisible);
+ pw.println(" mSurfaceAlpha=" + mSurfaceAlpha);
+ }
+
+ /**
+ * Keeps a surface aligned with the natural orientation of the device.
+ * Updates the position and transformation of the matrix whenever the display
+ * is rotated. This is a little tricky because the display transaction
+ * callback can be invoked on any thread, not necessarily the thread that
+ * owns the electron beam.
+ */
+ private static final class NaturalSurfaceLayout implements DisplayTransactionListener {
+ private final DisplayManagerService mDisplayManager;
+ private SurfaceControl mSurfaceControl;
+
+ public NaturalSurfaceLayout(DisplayManagerService displayManager, SurfaceControl surfaceControl) {
+ mDisplayManager = displayManager;
+ mSurfaceControl = surfaceControl;
+ mDisplayManager.registerDisplayTransactionListener(this);
+ }
+
+ public void dispose() {
+ synchronized (this) {
+ mSurfaceControl = null;
+ }
+ mDisplayManager.unregisterDisplayTransactionListener(this);
+ }
+
+ @Override
+ public void onDisplayTransaction() {
+ synchronized (this) {
+ if (mSurfaceControl == null) {
+ return;
+ }
+
+ DisplayInfo displayInfo = mDisplayManager.getDisplayInfo(Display.DEFAULT_DISPLAY);
+ switch (displayInfo.rotation) {
+ case Surface.ROTATION_0:
+ mSurfaceControl.setPosition(0, 0);
+ mSurfaceControl.setMatrix(1, 0, 0, 1);
+ break;
+ case Surface.ROTATION_90:
+ mSurfaceControl.setPosition(0, displayInfo.logicalHeight);
+ mSurfaceControl.setMatrix(0, -1, 1, 0);
+ break;
+ case Surface.ROTATION_180:
+ mSurfaceControl.setPosition(displayInfo.logicalWidth, displayInfo.logicalHeight);
+ mSurfaceControl.setMatrix(-1, 0, 0, -1);
+ break;
+ case Surface.ROTATION_270:
+ mSurfaceControl.setPosition(displayInfo.logicalWidth, 0);
+ mSurfaceControl.setMatrix(0, 1, -1, 0);
+ break;
+ }
+ }
+ }
+ }
+}