ColorFade.java revision 0839adb25c6d1a9f4e06fc5a098ffd03c67dbe99
1/*
2 * Copyright (C) 2014 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.display;
18
19import java.io.IOException;
20import java.io.InputStream;
21import java.io.InputStreamReader;
22import java.io.PrintWriter;
23import java.nio.ByteBuffer;
24import java.nio.ByteOrder;
25import java.nio.FloatBuffer;
26
27import android.content.Context;
28import android.content.res.Resources;
29import android.graphics.PixelFormat;
30import android.graphics.SurfaceTexture;
31import android.hardware.display.DisplayManagerInternal;
32import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
33import android.opengl.EGL14;
34import android.opengl.EGLConfig;
35import android.opengl.EGLContext;
36import android.opengl.EGLDisplay;
37import android.opengl.EGLSurface;
38import android.opengl.GLES20;
39import android.opengl.GLES11Ext;
40import android.util.FloatMath;
41import android.util.Slog;
42import android.view.DisplayInfo;
43import android.view.Surface.OutOfResourcesException;
44import android.view.Surface;
45import android.view.SurfaceControl;
46import android.view.SurfaceSession;
47
48import libcore.io.Streams;
49
50import com.android.server.LocalServices;
51import com.android.internal.R;
52
53/**
54 * <p>
55 * Animates a screen transition from on to off or off to on by applying
56 * some GL transformations to a screenshot.
57 * </p><p>
58 * This component must only be created or accessed by the {@link Looper} thread
59 * that belongs to the {@link DisplayPowerController}.
60 * </p>
61 */
62final class ColorFade {
63    private static final String TAG = "ColorFade";
64
65    private static final boolean DEBUG = false;
66
67    // The layer for the electron beam surface.
68    // This is currently hardcoded to be one layer above the boot animation.
69    private static final int COLOR_FADE_LAYER = 0x40000001;
70
71    // The number of frames to draw when preparing the animation so that it will
72    // be ready to run smoothly.  We use 3 frames because we are triple-buffered.
73    // See code for details.
74    private static final int DEJANK_FRAMES = 3;
75
76    private final int mDisplayId;
77
78    // Set to true when the animation context has been fully prepared.
79    private boolean mPrepared;
80    private int mMode;
81
82    private final DisplayManagerInternal mDisplayManagerInternal;
83    private int mDisplayLayerStack; // layer stack associated with primary display
84    private int mDisplayWidth;      // real width, not rotated
85    private int mDisplayHeight;     // real height, not rotated
86    private SurfaceSession mSurfaceSession;
87    private SurfaceControl mSurfaceControl;
88    private Surface mSurface;
89    private NaturalSurfaceLayout mSurfaceLayout;
90    private EGLDisplay mEglDisplay;
91    private EGLConfig mEglConfig;
92    private EGLContext mEglContext;
93    private EGLSurface mEglSurface;
94    private boolean mSurfaceVisible;
95    private float mSurfaceAlpha;
96
97    // Texture names.  We only use one texture, which contains the screenshot.
98    private final int[] mTexNames = new int[1];
99    private boolean mTexNamesGenerated;
100    private final float mTexMatrix[] = new float[16];
101    private final float mProjMatrix[] = new float[16];
102    private final int[] mGLBuffers = new int[2];
103    private int mTexCoordLoc, mVertexLoc, mTexUnitLoc, mProjMatrixLoc, mTexMatrixLoc;
104    private int mOpacityLoc, mScaleLoc, mGammaLoc, mSaturationLoc;
105    private int mProgram;
106
107    // Vertex and corresponding texture coordinates.
108    // We have 4 2D vertices, so 8 elements.  The vertices form a quad.
109    private final FloatBuffer mVertexBuffer = createNativeFloatBuffer(8);
110    private final FloatBuffer mTexCoordBuffer = createNativeFloatBuffer(8);
111
112    /**
113     * Animates an color fade warming up.
114     */
115    public static final int MODE_WARM_UP = 0;
116
117    /**
118     * Animates an color fade shutting off.
119     */
120    public static final int MODE_COOL_DOWN = 1;
121
122    /**
123     * Animates a simple dim layer to fade the contents of the screen in or out progressively.
124     */
125    public static final int MODE_FADE = 2;
126
127    public ColorFade(int displayId) {
128        mDisplayId = displayId;
129        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
130    }
131
132    /**
133     * Warms up the color fade in preparation for turning on or off.
134     * This method prepares a GL context, and captures a screen shot.
135     *
136     * @param mode The desired mode for the upcoming animation.
137     * @return True if the color fade is ready, false if it is uncontrollable.
138     */
139    public boolean prepare(Context context, int mode) {
140        if (DEBUG) {
141            Slog.d(TAG, "prepare: mode=" + mode);
142        }
143
144        mMode = mode;
145
146        // Get the display size and layer stack.
147        // This is not expected to change while the color fade surface is showing.
148        DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId);
149        mDisplayLayerStack = displayInfo.layerStack;
150        mDisplayWidth = displayInfo.getNaturalWidth();
151        mDisplayHeight = displayInfo.getNaturalHeight();
152
153        // Prepare the surface for drawing.
154        if (!(createSurface() && createEglContext() && createEglSurface() &&
155              captureScreenshotTextureAndSetViewport())) {
156            dismiss();
157            return false;
158        }
159
160        // Init GL
161        if (!attachEglContext()) {
162            return false;
163        }
164        try {
165            if(!initGLShaders(context) || !initGLBuffers() || checkGlErrors("prepare")) {
166                detachEglContext();
167                dismiss();
168                return false;
169            }
170        } finally {
171            detachEglContext();
172        }
173
174        // Done.
175        mPrepared = true;
176
177        // Dejanking optimization.
178        // Some GL drivers can introduce a lot of lag in the first few frames as they
179        // initialize their state and allocate graphics buffers for rendering.
180        // Work around this problem by rendering the first frame of the animation a few
181        // times.  The rest of the animation should run smoothly thereafter.
182        // The frames we draw here aren't visible because we are essentially just
183        // painting the screenshot as-is.
184        if (mode == MODE_COOL_DOWN) {
185            for (int i = 0; i < DEJANK_FRAMES; i++) {
186                draw(1.0f);
187            }
188        }
189        return true;
190    }
191
192    private String readFile(Context context, int resourceId) {
193        try{
194            InputStream stream = context.getResources().openRawResource(resourceId);
195            return new String(Streams.readFully(new InputStreamReader(stream)));
196        }
197        catch (IOException e) {
198            Slog.e(TAG, "Unrecognized shader " + Integer.toString(resourceId));
199            throw new RuntimeException(e);
200        }
201    }
202
203    private int loadShader(Context context, int resourceId, int type) {
204        String source = readFile(context, resourceId);
205
206        int shader = GLES20.glCreateShader(type);
207
208        GLES20.glShaderSource(shader, source);
209        GLES20.glCompileShader(shader);
210
211        int[] compiled = new int[1];
212        GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
213        if (compiled[0] == 0) {
214            Slog.e(TAG, "Could not compile shader " + shader + ", " + type + ":");
215            Slog.e(TAG, GLES20.glGetShaderSource(shader));
216            Slog.e(TAG, GLES20.glGetShaderInfoLog(shader));
217            GLES20.glDeleteShader(shader);
218            shader = 0;
219        }
220
221        return shader;
222    }
223
224    private boolean initGLShaders(Context context) {
225        int vshader = loadShader(context, com.android.internal.R.raw.color_fade_vert,
226                GLES20.GL_VERTEX_SHADER);
227        int fshader = loadShader(context, com.android.internal.R.raw.color_fade_frag,
228                GLES20.GL_FRAGMENT_SHADER);
229        if (vshader == 0 || fshader == 0) return false;
230
231        mProgram = GLES20.glCreateProgram();
232
233        GLES20.glAttachShader(mProgram, vshader);
234        GLES20.glAttachShader(mProgram, fshader);
235
236        GLES20.glLinkProgram(mProgram);
237
238        mVertexLoc = GLES20.glGetAttribLocation(mProgram, "position");
239        mTexCoordLoc = GLES20.glGetAttribLocation(mProgram, "uv");
240
241        mProjMatrixLoc = GLES20.glGetUniformLocation(mProgram, "proj_matrix");
242        mTexMatrixLoc = GLES20.glGetUniformLocation(mProgram, "tex_matrix");
243
244        mOpacityLoc = GLES20.glGetUniformLocation(mProgram, "opacity");
245        mGammaLoc = GLES20.glGetUniformLocation(mProgram, "gamma");
246        mSaturationLoc = GLES20.glGetUniformLocation(mProgram, "saturation");
247        mScaleLoc = GLES20.glGetUniformLocation(mProgram, "scale");
248        mTexUnitLoc = GLES20.glGetUniformLocation(mProgram, "texUnit");
249
250        GLES20.glUseProgram(mProgram);
251        GLES20.glUniform1i(mTexUnitLoc, 0);
252        GLES20.glUseProgram(0);
253
254        return true;
255    }
256
257    private boolean initGLBuffers() {
258        //Fill vertices
259        setQuad(mVertexBuffer, 0, 0, mDisplayWidth, mDisplayHeight);
260
261        // Setup GL Textures
262        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTexNames[0]);
263        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
264                GLES20.GL_NEAREST);
265        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
266                GLES20.GL_NEAREST);
267        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
268                GLES20.GL_CLAMP_TO_EDGE);
269        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
270                GLES20.GL_CLAMP_TO_EDGE);
271        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
272
273        // Setup GL Buffers
274        GLES20.glGenBuffers(2, mGLBuffers, 0);
275
276        // fill vertex buffer
277        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLBuffers[0]);
278        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, mVertexBuffer.capacity() * 4,
279                            mVertexBuffer, GLES20.GL_STATIC_DRAW);
280
281        // fill tex buffer
282        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLBuffers[1]);
283        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, mTexCoordBuffer.capacity() * 4,
284                            mTexCoordBuffer, GLES20.GL_STATIC_DRAW);
285
286        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
287
288        return true;
289    }
290
291    private static void setQuad(FloatBuffer vtx, float x, float y, float w, float h) {
292        if (DEBUG) {
293            Slog.d(TAG, "setQuad: x=" + x + ", y=" + y + ", w=" + w + ", h=" + h);
294        }
295        vtx.put(0, x);
296        vtx.put(1, y);
297        vtx.put(2, x);
298        vtx.put(3, y + h);
299        vtx.put(4, x + w);
300        vtx.put(5, y + h);
301        vtx.put(6, x + w);
302        vtx.put(7, y);
303    }
304
305    /**
306     * Dismisses the color fade animation surface and cleans up.
307     *
308     * To prevent stray photons from leaking out after the color fade has been
309     * turned off, it is a good idea to defer dismissing the animation until the
310     * color fade has been turned back on fully.
311     */
312    public void dismiss() {
313        if (DEBUG) {
314            Slog.d(TAG, "dismiss");
315        }
316
317        destroyScreenshotTexture();
318        destroyEglSurface();
319        destroySurface();
320        mPrepared = false;
321    }
322
323    /**
324     * Draws an animation frame showing the color fade activated at the
325     * specified level.
326     *
327     * @param level The color fade level.
328     * @return True if successful.
329     */
330    public boolean draw(float level) {
331        if (DEBUG) {
332            Slog.d(TAG, "drawFrame: level=" + level);
333        }
334
335        if (!mPrepared) {
336            return false;
337        }
338
339        if (mMode == MODE_FADE) {
340            return showSurface(1.0f - level);
341        }
342
343        if (!attachEglContext()) {
344            return false;
345        }
346        try {
347            // Clear frame to solid black.
348            GLES20.glClearColor(0f, 0f, 0f, 1f);
349            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
350
351            // Draw the frame.
352            float one_minus_level = 1 - level;
353            float cos = FloatMath.cos((float)Math.PI * one_minus_level);
354            float sign = cos < 0 ? -1 : 1;
355            float opacity = -FloatMath.pow(one_minus_level, 2) + 1;
356            float saturation = FloatMath.pow(level, 4);
357            float scale = (-FloatMath.pow(one_minus_level, 2) + 1) * 0.1f + 0.9f;
358            float gamma = (0.5f * sign * FloatMath.pow(cos, 2) + 0.5f) * 0.9f + 0.1f;
359            drawFaded(opacity, 1.f / gamma, saturation, scale);
360            if (checkGlErrors("drawFrame")) {
361                return false;
362            }
363
364            EGL14.eglSwapBuffers(mEglDisplay, mEglSurface);
365        } finally {
366            detachEglContext();
367        }
368        return showSurface(1.0f);
369    }
370
371    private void drawFaded(float opacity, float gamma, float saturation, float scale) {
372        if (DEBUG) {
373            Slog.d(TAG, "drawFaded: opacity=" + opacity + ", gamma=" + gamma +
374                        ", saturation=" + saturation + ", scale=" + scale);
375        }
376        // Use shaders
377        GLES20.glUseProgram(mProgram);
378
379        // Set Uniforms
380        GLES20.glUniformMatrix4fv(mProjMatrixLoc, 1, false, mProjMatrix, 0);
381        GLES20.glUniformMatrix4fv(mTexMatrixLoc, 1, false, mTexMatrix, 0);
382        GLES20.glUniform1f(mOpacityLoc, opacity);
383        GLES20.glUniform1f(mGammaLoc, gamma);
384        GLES20.glUniform1f(mSaturationLoc, saturation);
385        GLES20.glUniform1f(mScaleLoc, scale);
386
387        // Use textures
388        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
389        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTexNames[0]);
390
391        // draw the plane
392        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLBuffers[0]);
393        GLES20.glEnableVertexAttribArray(mVertexLoc);
394        GLES20.glVertexAttribPointer(mVertexLoc, 2, GLES20.GL_FLOAT, false, 0, 0);
395
396        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLBuffers[1]);
397        GLES20.glEnableVertexAttribArray(mTexCoordLoc);
398        GLES20.glVertexAttribPointer(mTexCoordLoc, 2, GLES20.GL_FLOAT, false, 0, 0);
399
400        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 4);
401
402        // clean up
403        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
404        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
405    }
406
407    private void ortho(float left, float right, float bottom, float top, float znear, float zfar) {
408        mProjMatrix[0] = 2f / (right - left);
409        mProjMatrix[1] = 0;
410        mProjMatrix[2] = 0;
411        mProjMatrix[3] = 0;
412        mProjMatrix[4] = 0;
413        mProjMatrix[5] = 2f / (top - bottom);
414        mProjMatrix[6] = 0;
415        mProjMatrix[7] = 0;
416        mProjMatrix[8] = 0;
417        mProjMatrix[9] = 0;
418        mProjMatrix[10] = -2f / (zfar - znear);
419        mProjMatrix[11] = 0;
420        mProjMatrix[12] = -(right + left) / (right - left);
421        mProjMatrix[13] = -(top + bottom) / (top - bottom);
422        mProjMatrix[14] = -(zfar + znear) / (zfar - znear);
423        mProjMatrix[15] = 1f;
424    }
425
426    private boolean captureScreenshotTextureAndSetViewport() {
427        if (!attachEglContext()) {
428            return false;
429        }
430        try {
431            if (!mTexNamesGenerated) {
432                GLES20.glGenTextures(1, mTexNames, 0);
433                if (checkGlErrors("glGenTextures")) {
434                    return false;
435                }
436                mTexNamesGenerated = true;
437            }
438
439            final SurfaceTexture st = new SurfaceTexture(mTexNames[0]);
440            final Surface s = new Surface(st);
441            try {
442                SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay(
443                        SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN), s);
444            } finally {
445                s.release();
446            }
447
448            st.updateTexImage();
449            st.getTransformMatrix(mTexMatrix);
450
451            // Set up texture coordinates for a quad.
452            // We might need to change this if the texture ends up being
453            // a different size from the display for some reason.
454            mTexCoordBuffer.put(0, 0f); mTexCoordBuffer.put(1, 0f);
455            mTexCoordBuffer.put(2, 0f); mTexCoordBuffer.put(3, 1f);
456            mTexCoordBuffer.put(4, 1f); mTexCoordBuffer.put(5, 1f);
457            mTexCoordBuffer.put(6, 1f); mTexCoordBuffer.put(7, 0f);
458
459            // Set up our viewport.
460            GLES20.glViewport(0, 0, mDisplayWidth, mDisplayHeight);
461            ortho(0, mDisplayWidth, 0, mDisplayHeight, -1, 1);
462        } finally {
463            detachEglContext();
464        }
465        return true;
466    }
467
468    private void destroyScreenshotTexture() {
469        if (mTexNamesGenerated) {
470            mTexNamesGenerated = false;
471            if (attachEglContext()) {
472                try {
473                    GLES20.glDeleteTextures(1, mTexNames, 0);
474                    checkGlErrors("glDeleteTextures");
475                } finally {
476                    detachEglContext();
477                }
478            }
479        }
480    }
481
482    private boolean createEglContext() {
483        if (mEglDisplay == null) {
484            mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
485            if (mEglDisplay == EGL14.EGL_NO_DISPLAY) {
486                logEglError("eglGetDisplay");
487                return false;
488            }
489
490            int[] version = new int[2];
491            if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) {
492                mEglDisplay = null;
493                logEglError("eglInitialize");
494                return false;
495            }
496        }
497
498        if (mEglConfig == null) {
499            int[] eglConfigAttribList = new int[] {
500                    EGL14.EGL_RENDERABLE_TYPE,
501                    EGL14.EGL_OPENGL_ES2_BIT,
502                    EGL14.EGL_RED_SIZE, 8,
503                    EGL14.EGL_GREEN_SIZE, 8,
504                    EGL14.EGL_BLUE_SIZE, 8,
505                    EGL14.EGL_ALPHA_SIZE, 8,
506                    EGL14.EGL_NONE
507            };
508            int[] numEglConfigs = new int[1];
509            EGLConfig[] eglConfigs = new EGLConfig[1];
510            if (!EGL14.eglChooseConfig(mEglDisplay, eglConfigAttribList, 0,
511                    eglConfigs, 0, eglConfigs.length, numEglConfigs, 0)) {
512                logEglError("eglChooseConfig");
513                return false;
514            }
515            mEglConfig = eglConfigs[0];
516        }
517
518        if (mEglContext == null) {
519            int[] eglContextAttribList = new int[] {
520                    EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
521                    EGL14.EGL_NONE
522            };
523            mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig,
524                    EGL14.EGL_NO_CONTEXT, eglContextAttribList, 0);
525            if (mEglContext == null) {
526                logEglError("eglCreateContext");
527                return false;
528            }
529        }
530        return true;
531    }
532
533    private boolean createSurface() {
534        if (mSurfaceSession == null) {
535            mSurfaceSession = new SurfaceSession();
536        }
537
538        SurfaceControl.openTransaction();
539        try {
540            if (mSurfaceControl == null) {
541                try {
542                    int flags;
543                    if (mMode == MODE_FADE) {
544                        flags = SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN;
545                    } else {
546                        flags = SurfaceControl.OPAQUE | SurfaceControl.HIDDEN;
547                    }
548                    mSurfaceControl = new SurfaceControl(mSurfaceSession,
549                            "ColorFade", mDisplayWidth, mDisplayHeight,
550                            PixelFormat.OPAQUE, flags);
551                } catch (OutOfResourcesException ex) {
552                    Slog.e(TAG, "Unable to create surface.", ex);
553                    return false;
554                }
555            }
556
557            mSurfaceControl.setLayerStack(mDisplayLayerStack);
558            mSurfaceControl.setSize(mDisplayWidth, mDisplayHeight);
559            mSurface = new Surface();
560            mSurface.copyFrom(mSurfaceControl);
561
562            mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal,
563                    mDisplayId, mSurfaceControl);
564            mSurfaceLayout.onDisplayTransaction();
565        } finally {
566            SurfaceControl.closeTransaction();
567        }
568        return true;
569    }
570
571    private boolean createEglSurface() {
572        if (mEglSurface == null) {
573            int[] eglSurfaceAttribList = new int[] {
574                    EGL14.EGL_NONE
575            };
576            // turn our SurfaceControl into a Surface
577            mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface,
578                    eglSurfaceAttribList, 0);
579            if (mEglSurface == null) {
580                logEglError("eglCreateWindowSurface");
581                return false;
582            }
583        }
584        return true;
585    }
586
587    private void destroyEglSurface() {
588        if (mEglSurface != null) {
589            if (!EGL14.eglDestroySurface(mEglDisplay, mEglSurface)) {
590                logEglError("eglDestroySurface");
591            }
592            mEglSurface = null;
593        }
594    }
595
596    private void destroySurface() {
597        if (mSurfaceControl != null) {
598            mSurfaceLayout.dispose();
599            mSurfaceLayout = null;
600            SurfaceControl.openTransaction();
601            try {
602                mSurfaceControl.destroy();
603                mSurface.release();
604            } finally {
605                SurfaceControl.closeTransaction();
606            }
607            mSurfaceControl = null;
608            mSurfaceVisible = false;
609            mSurfaceAlpha = 0f;
610        }
611    }
612
613    private boolean showSurface(float alpha) {
614        if (!mSurfaceVisible || mSurfaceAlpha != alpha) {
615            SurfaceControl.openTransaction();
616            try {
617                mSurfaceControl.setLayer(COLOR_FADE_LAYER);
618                mSurfaceControl.setAlpha(alpha);
619                mSurfaceControl.show();
620            } finally {
621                SurfaceControl.closeTransaction();
622            }
623            mSurfaceVisible = true;
624            mSurfaceAlpha = alpha;
625        }
626        return true;
627    }
628
629    private boolean attachEglContext() {
630        if (mEglSurface == null) {
631            return false;
632        }
633        if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
634            logEglError("eglMakeCurrent");
635            return false;
636        }
637        return true;
638    }
639
640    private void detachEglContext() {
641        if (mEglDisplay != null) {
642            EGL14.eglMakeCurrent(mEglDisplay,
643                    EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
644        }
645    }
646
647    private static FloatBuffer createNativeFloatBuffer(int size) {
648        ByteBuffer bb = ByteBuffer.allocateDirect(size * 4);
649        bb.order(ByteOrder.nativeOrder());
650        return bb.asFloatBuffer();
651    }
652
653    private static void logEglError(String func) {
654        Slog.e(TAG, func + " failed: error " + EGL14.eglGetError(), new Throwable());
655    }
656
657    private static boolean checkGlErrors(String func) {
658        return checkGlErrors(func, true);
659    }
660
661    private static boolean checkGlErrors(String func, boolean log) {
662        boolean hadError = false;
663        int error;
664        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
665            if (log) {
666                Slog.e(TAG, func + " failed: error " + error, new Throwable());
667            }
668            hadError = true;
669        }
670        return hadError;
671    }
672
673    public void dump(PrintWriter pw) {
674        pw.println();
675        pw.println("Color Fade State:");
676        pw.println("  mPrepared=" + mPrepared);
677        pw.println("  mMode=" + mMode);
678        pw.println("  mDisplayLayerStack=" + mDisplayLayerStack);
679        pw.println("  mDisplayWidth=" + mDisplayWidth);
680        pw.println("  mDisplayHeight=" + mDisplayHeight);
681        pw.println("  mSurfaceVisible=" + mSurfaceVisible);
682        pw.println("  mSurfaceAlpha=" + mSurfaceAlpha);
683    }
684
685    /**
686     * Keeps a surface aligned with the natural orientation of the device.
687     * Updates the position and transformation of the matrix whenever the display
688     * is rotated.  This is a little tricky because the display transaction
689     * callback can be invoked on any thread, not necessarily the thread that
690     * owns the color fade.
691     */
692    private static final class NaturalSurfaceLayout implements DisplayTransactionListener {
693        private final DisplayManagerInternal mDisplayManagerInternal;
694        private final int mDisplayId;
695        private SurfaceControl mSurfaceControl;
696
697        public NaturalSurfaceLayout(DisplayManagerInternal displayManagerInternal,
698                int displayId, SurfaceControl surfaceControl) {
699            mDisplayManagerInternal = displayManagerInternal;
700            mDisplayId = displayId;
701            mSurfaceControl = surfaceControl;
702            mDisplayManagerInternal.registerDisplayTransactionListener(this);
703        }
704
705        public void dispose() {
706            synchronized (this) {
707                mSurfaceControl = null;
708            }
709            mDisplayManagerInternal.unregisterDisplayTransactionListener(this);
710        }
711
712        @Override
713        public void onDisplayTransaction() {
714            synchronized (this) {
715                if (mSurfaceControl == null) {
716                    return;
717                }
718
719                DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId);
720                switch (displayInfo.rotation) {
721                    case Surface.ROTATION_0:
722                        mSurfaceControl.setPosition(0, 0);
723                        mSurfaceControl.setMatrix(1, 0, 0, 1);
724                        break;
725                    case Surface.ROTATION_90:
726                        mSurfaceControl.setPosition(0, displayInfo.logicalHeight);
727                        mSurfaceControl.setMatrix(0, -1, 1, 0);
728                        break;
729                    case Surface.ROTATION_180:
730                        mSurfaceControl.setPosition(displayInfo.logicalWidth,
731                                displayInfo.logicalHeight);
732                        mSurfaceControl.setMatrix(-1, 0, 0, -1);
733                        break;
734                    case Surface.ROTATION_270:
735                        mSurfaceControl.setPosition(displayInfo.logicalWidth, 0);
736                        mSurfaceControl.setMatrix(0, 1, -1, 0);
737                        break;
738                }
739            }
740        }
741    }
742}
743