FileDocCategorySizeDatePackage
ColorFade.javaAPI DocAndroid 5.1 API27527Thu Mar 12 22:22:42 GMT 2015com.android.server.display

ColorFade

public final class ColorFade extends Object

Animates a screen transition from on to off or off to on by applying some GL transformations to a screenshot.

This component must only be created or accessed by the {@link Looper} thread that belongs to the {@link DisplayPowerController}.

Fields Summary
private static final String
TAG
private static final boolean
DEBUG
private static final int
COLOR_FADE_LAYER
private static final int
DEJANK_FRAMES
private final int
mDisplayId
private boolean
mPrepared
private int
mMode
private final android.hardware.display.DisplayManagerInternal
mDisplayManagerInternal
private int
mDisplayLayerStack
private int
mDisplayWidth
private int
mDisplayHeight
private android.view.SurfaceSession
mSurfaceSession
private android.view.SurfaceControl
mSurfaceControl
private android.view.Surface
mSurface
private NaturalSurfaceLayout
mSurfaceLayout
private android.opengl.EGLDisplay
mEglDisplay
private android.opengl.EGLConfig
mEglConfig
private android.opengl.EGLContext
mEglContext
private android.opengl.EGLSurface
mEglSurface
private boolean
mSurfaceVisible
private float
mSurfaceAlpha
private final int[]
mTexNames
private boolean
mTexNamesGenerated
private final float[]
mTexMatrix
private final float[]
mProjMatrix
private final int[]
mGLBuffers
private int
mTexCoordLoc
private int
mVertexLoc
private int
mTexUnitLoc
private int
mProjMatrixLoc
private int
mTexMatrixLoc
private int
mOpacityLoc
private int
mScaleLoc
private int
mGammaLoc
private int
mSaturationLoc
private int
mProgram
private final FloatBuffer
mVertexBuffer
private final FloatBuffer
mTexCoordBuffer
public static final int
MODE_WARM_UP
Animates an color fade warming up.
public static final int
MODE_COOL_DOWN
Animates an color fade shutting off.
public static final int
MODE_FADE
Animates a simple dim layer to fade the contents of the screen in or out progressively.
Constructors Summary
public ColorFade(int displayId)


       
        mDisplayId = displayId;
        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
    
Methods Summary
private booleanattachEglContext()

        if (mEglSurface == null) {
            return false;
        }
        if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
            logEglError("eglMakeCurrent");
            return false;
        }
        return true;
    
private booleancaptureScreenshotTextureAndSetViewport()

        if (!attachEglContext()) {
            return false;
        }
        try {
            if (!mTexNamesGenerated) {
                GLES20.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);
                st.updateTexImage();
                st.getTransformMatrix(mTexMatrix);
            } finally {
                s.release();
                st.release();
            }

            // 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.
            GLES20.glViewport(0, 0, mDisplayWidth, mDisplayHeight);
            ortho(0, mDisplayWidth, 0, mDisplayHeight, -1, 1);
        } finally {
            detachEglContext();
        }
        return true;
    
private static booleancheckGlErrors(java.lang.String func)

        return checkGlErrors(func, true);
    
private static booleancheckGlErrors(java.lang.String func, boolean log)

        boolean hadError = false;
        int error;
        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
            if (log) {
                Slog.e(TAG, func + " failed: error " + error, new Throwable());
            }
            hadError = true;
        }
        return hadError;
    
private booleancreateEglContext()

        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_RENDERABLE_TYPE,
                    EGL14.EGL_OPENGL_ES2_BIT,
                    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_CONTEXT_CLIENT_VERSION, 2,
                    EGL14.EGL_NONE
            };
            mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig,
                    EGL14.EGL_NO_CONTEXT, eglContextAttribList, 0);
            if (mEglContext == null) {
                logEglError("eglCreateContext");
                return false;
            }
        }
        return true;
    
private booleancreateEglSurface()

        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 static java.nio.FloatBuffercreateNativeFloatBuffer(int size)

        ByteBuffer bb = ByteBuffer.allocateDirect(size * 4);
        bb.order(ByteOrder.nativeOrder());
        return bb.asFloatBuffer();
    
private booleancreateSurface()

        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,
                            "ColorFade", 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(mDisplayManagerInternal,
                    mDisplayId, mSurfaceControl);
            mSurfaceLayout.onDisplayTransaction();
        } finally {
            SurfaceControl.closeTransaction();
        }
        return true;
    
private voiddestroyEglSurface()

        if (mEglSurface != null) {
            if (!EGL14.eglDestroySurface(mEglDisplay, mEglSurface)) {
                logEglError("eglDestroySurface");
            }
            mEglSurface = null;
        }
    
private voiddestroyGLBuffers()

        GLES20.glDeleteBuffers(2, mGLBuffers, 0);
        checkGlErrors("glDeleteBuffers");
    
private voiddestroyGLShaders()

        GLES20.glDeleteProgram(mProgram);
        checkGlErrors("glDeleteProgram");
    
private voiddestroyScreenshotTexture()

        if (mTexNamesGenerated) {
            mTexNamesGenerated = false;
            GLES20.glDeleteTextures(1, mTexNames, 0);
            checkGlErrors("glDeleteTextures");
        }
    
private voiddestroySurface()

        if (mSurfaceControl != null) {
            mSurfaceLayout.dispose();
            mSurfaceLayout = null;
            SurfaceControl.openTransaction();
            try {
                mSurfaceControl.destroy();
                mSurface.release();
            } finally {
                SurfaceControl.closeTransaction();
            }
            mSurfaceControl = null;
            mSurfaceVisible = false;
            mSurfaceAlpha = 0f;
        }
    
private voiddetachEglContext()

        if (mEglDisplay != null) {
            EGL14.eglMakeCurrent(mEglDisplay,
                    EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
        }
    
public voiddismiss()
Dismisses the color fade animation surface and cleans up. To prevent stray photons from leaking out after the color fade has been turned off, it is a good idea to defer dismissing the animation until the color fade has been turned back on fully.

        if (DEBUG) {
            Slog.d(TAG, "dismiss");
        }

        if (mPrepared) {
            attachEglContext();
            try {
                destroyScreenshotTexture();
                destroyGLShaders();
                destroyGLBuffers();
                destroyEglSurface();
            } finally {
                detachEglContext();
            }
            destroySurface();
            GLES20.glFlush();
            mPrepared = false;
        }
    
public booleandraw(float level)
Draws an animation frame showing the color fade activated at the specified level.

param
level The color fade level.
return
True if successful.

        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.
            GLES20.glClearColor(0f, 0f, 0f, 1f);
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

            // Draw the frame.
            float one_minus_level = 1 - level;
            float cos = FloatMath.cos((float)Math.PI * one_minus_level);
            float sign = cos < 0 ? -1 : 1;
            float opacity = -FloatMath.pow(one_minus_level, 2) + 1;
            float saturation = FloatMath.pow(level, 4);
            float scale = (-FloatMath.pow(one_minus_level, 2) + 1) * 0.1f + 0.9f;
            float gamma = (0.5f * sign * FloatMath.pow(cos, 2) + 0.5f) * 0.9f + 0.1f;
            drawFaded(opacity, 1.f / gamma, saturation, scale);
            if (checkGlErrors("drawFrame")) {
                return false;
            }

            EGL14.eglSwapBuffers(mEglDisplay, mEglSurface);
        } finally {
            detachEglContext();
        }
        return showSurface(1.0f);
    
private voiddrawFaded(float opacity, float gamma, float saturation, float scale)

        if (DEBUG) {
            Slog.d(TAG, "drawFaded: opacity=" + opacity + ", gamma=" + gamma +
                        ", saturation=" + saturation + ", scale=" + scale);
        }
        // Use shaders
        GLES20.glUseProgram(mProgram);

        // Set Uniforms
        GLES20.glUniformMatrix4fv(mProjMatrixLoc, 1, false, mProjMatrix, 0);
        GLES20.glUniformMatrix4fv(mTexMatrixLoc, 1, false, mTexMatrix, 0);
        GLES20.glUniform1f(mOpacityLoc, opacity);
        GLES20.glUniform1f(mGammaLoc, gamma);
        GLES20.glUniform1f(mSaturationLoc, saturation);
        GLES20.glUniform1f(mScaleLoc, scale);

        // Use textures
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTexNames[0]);

        // draw the plane
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLBuffers[0]);
        GLES20.glEnableVertexAttribArray(mVertexLoc);
        GLES20.glVertexAttribPointer(mVertexLoc, 2, GLES20.GL_FLOAT, false, 0, 0);

        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLBuffers[1]);
        GLES20.glEnableVertexAttribArray(mTexCoordLoc);
        GLES20.glVertexAttribPointer(mTexCoordLoc, 2, GLES20.GL_FLOAT, false, 0, 0);

        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 4);

        // clean up
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
    
public voiddump(java.io.PrintWriter pw)

        pw.println();
        pw.println("Color Fade 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);
    
private booleaninitGLBuffers()

        //Fill vertices
        setQuad(mVertexBuffer, 0, 0, mDisplayWidth, mDisplayHeight);

        // Setup GL Textures
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTexNames[0]);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
                GLES20.GL_NEAREST);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
                GLES20.GL_NEAREST);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
                GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
                GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);

        // Setup GL Buffers
        GLES20.glGenBuffers(2, mGLBuffers, 0);

        // fill vertex buffer
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLBuffers[0]);
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, mVertexBuffer.capacity() * 4,
                            mVertexBuffer, GLES20.GL_STATIC_DRAW);

        // fill tex buffer
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLBuffers[1]);
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, mTexCoordBuffer.capacity() * 4,
                            mTexCoordBuffer, GLES20.GL_STATIC_DRAW);

        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);

        return true;
    
private booleaninitGLShaders(android.content.Context context)

        int vshader = loadShader(context, com.android.internal.R.raw.color_fade_vert,
                GLES20.GL_VERTEX_SHADER);
        int fshader = loadShader(context, com.android.internal.R.raw.color_fade_frag,
                GLES20.GL_FRAGMENT_SHADER);
        GLES20.glReleaseShaderCompiler();
        if (vshader == 0 || fshader == 0) return false;

        mProgram = GLES20.glCreateProgram();

        GLES20.glAttachShader(mProgram, vshader);
        GLES20.glAttachShader(mProgram, fshader);
        GLES20.glDeleteShader(vshader);
        GLES20.glDeleteShader(fshader);

        GLES20.glLinkProgram(mProgram);

        mVertexLoc = GLES20.glGetAttribLocation(mProgram, "position");
        mTexCoordLoc = GLES20.glGetAttribLocation(mProgram, "uv");

        mProjMatrixLoc = GLES20.glGetUniformLocation(mProgram, "proj_matrix");
        mTexMatrixLoc = GLES20.glGetUniformLocation(mProgram, "tex_matrix");

        mOpacityLoc = GLES20.glGetUniformLocation(mProgram, "opacity");
        mGammaLoc = GLES20.glGetUniformLocation(mProgram, "gamma");
        mSaturationLoc = GLES20.glGetUniformLocation(mProgram, "saturation");
        mScaleLoc = GLES20.glGetUniformLocation(mProgram, "scale");
        mTexUnitLoc = GLES20.glGetUniformLocation(mProgram, "texUnit");

        GLES20.glUseProgram(mProgram);
        GLES20.glUniform1i(mTexUnitLoc, 0);
        GLES20.glUseProgram(0);

        return true;
    
private intloadShader(android.content.Context context, int resourceId, int type)

        String source = readFile(context, resourceId);

        int shader = GLES20.glCreateShader(type);

        GLES20.glShaderSource(shader, source);
        GLES20.glCompileShader(shader);

        int[] compiled = new int[1];
        GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
        if (compiled[0] == 0) {
            Slog.e(TAG, "Could not compile shader " + shader + ", " + type + ":");
            Slog.e(TAG, GLES20.glGetShaderSource(shader));
            Slog.e(TAG, GLES20.glGetShaderInfoLog(shader));
            GLES20.glDeleteShader(shader);
            shader = 0;
        }

        return shader;
    
private static voidlogEglError(java.lang.String func)

        Slog.e(TAG, func + " failed: error " + EGL14.eglGetError(), new Throwable());
    
private voidortho(float left, float right, float bottom, float top, float znear, float zfar)

        mProjMatrix[0] = 2f / (right - left);
        mProjMatrix[1] = 0;
        mProjMatrix[2] = 0;
        mProjMatrix[3] = 0;
        mProjMatrix[4] = 0;
        mProjMatrix[5] = 2f / (top - bottom);
        mProjMatrix[6] = 0;
        mProjMatrix[7] = 0;
        mProjMatrix[8] = 0;
        mProjMatrix[9] = 0;
        mProjMatrix[10] = -2f / (zfar - znear);
        mProjMatrix[11] = 0;
        mProjMatrix[12] = -(right + left) / (right - left);
        mProjMatrix[13] = -(top + bottom) / (top - bottom);
        mProjMatrix[14] = -(zfar + znear) / (zfar - znear);
        mProjMatrix[15] = 1f;
    
public booleanprepare(android.content.Context context, int mode)
Warms up the color fade 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 color fade is ready, false if it is uncontrollable.

        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 color fade surface is showing.
        DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId);
        mDisplayLayerStack = displayInfo.layerStack;
        mDisplayWidth = displayInfo.getNaturalWidth();
        mDisplayHeight = displayInfo.getNaturalHeight();

        // Prepare the surface for drawing.
        if (!(createSurface() && createEglContext() && createEglSurface() &&
              captureScreenshotTextureAndSetViewport())) {
            dismiss();
            return false;
        }

        // Init GL
        if (!attachEglContext()) {
            return false;
        }
        try {
            if(!initGLShaders(context) || !initGLBuffers() || checkGlErrors("prepare")) {
                detachEglContext();
                dismiss();
                return false;
            }
        } finally {
            detachEglContext();
        }

        // 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 java.lang.StringreadFile(android.content.Context context, int resourceId)

        try{
            InputStream stream = context.getResources().openRawResource(resourceId);
            return new String(Streams.readFully(new InputStreamReader(stream)));
        }
        catch (IOException e) {
            Slog.e(TAG, "Unrecognized shader " + Integer.toString(resourceId));
            throw new RuntimeException(e);
        }
    
private static voidsetQuad(java.nio.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 booleanshowSurface(float alpha)

        if (!mSurfaceVisible || mSurfaceAlpha != alpha) {
            SurfaceControl.openTransaction();
            try {
                mSurfaceControl.setLayer(COLOR_FADE_LAYER);
                mSurfaceControl.setAlpha(alpha);
                mSurfaceControl.show();
            } finally {
                SurfaceControl.closeTransaction();
            }
            mSurfaceVisible = true;
            mSurfaceAlpha = alpha;
        }
        return true;