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