Home | History | Annotate | Download | only in systemui
      1 /*
      2  * Copyright (C) 2009 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 
     17 package com.android.systemui;
     18 
     19 import static android.opengl.GLES20.*;
     20 import static javax.microedition.khronos.egl.EGL10.*;
     21 
     22 import android.app.ActivityManager;
     23 import android.app.WallpaperManager;
     24 import android.content.BroadcastReceiver;
     25 import android.content.ComponentCallbacks2;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.graphics.Bitmap;
     29 import android.graphics.Canvas;
     30 import android.graphics.Rect;
     31 import android.graphics.Region.Op;
     32 import android.opengl.GLUtils;
     33 import android.os.SystemProperties;
     34 import android.renderscript.Matrix4f;
     35 import android.service.wallpaper.WallpaperService;
     36 import android.util.Log;
     37 import android.view.MotionEvent;
     38 import android.view.SurfaceHolder;
     39 import android.view.WindowManager;
     40 
     41 import java.io.IOException;
     42 import java.nio.ByteBuffer;
     43 import java.nio.ByteOrder;
     44 import java.nio.FloatBuffer;
     45 
     46 import javax.microedition.khronos.egl.EGL10;
     47 import javax.microedition.khronos.egl.EGLConfig;
     48 import javax.microedition.khronos.egl.EGLContext;
     49 import javax.microedition.khronos.egl.EGLDisplay;
     50 import javax.microedition.khronos.egl.EGLSurface;
     51 
     52 /**
     53  * Default built-in wallpaper that simply shows a static image.
     54  */
     55 @SuppressWarnings({"UnusedDeclaration"})
     56 public class ImageWallpaper extends WallpaperService {
     57     private static final String TAG = "ImageWallpaper";
     58     private static final String GL_LOG_TAG = "ImageWallpaperGL";
     59     private static final boolean DEBUG = false;
     60     private static final String PROPERTY_KERNEL_QEMU = "ro.kernel.qemu";
     61 
     62     static final boolean FIXED_SIZED_SURFACE = true;
     63     static final boolean USE_OPENGL = true;
     64 
     65     WallpaperManager mWallpaperManager;
     66 
     67     DrawableEngine mEngine;
     68 
     69     boolean mIsHwAccelerated;
     70 
     71     @Override
     72     public void onCreate() {
     73         super.onCreate();
     74         mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE);
     75 
     76         //noinspection PointlessBooleanExpression,ConstantConditions
     77         if (FIXED_SIZED_SURFACE && USE_OPENGL) {
     78             if (!isEmulator()) {
     79                 mIsHwAccelerated = ActivityManager.isHighEndGfx();
     80             }
     81         }
     82     }
     83 
     84     @Override
     85     public void onTrimMemory(int level) {
     86         if (mEngine != null) {
     87             mEngine.trimMemory(level);
     88         }
     89     }
     90 
     91     private static boolean isEmulator() {
     92         return "1".equals(SystemProperties.get(PROPERTY_KERNEL_QEMU, "0"));
     93     }
     94 
     95     @Override
     96     public Engine onCreateEngine() {
     97         mEngine = new DrawableEngine();
     98         return mEngine;
     99     }
    100 
    101     class DrawableEngine extends Engine {
    102         static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
    103         static final int EGL_OPENGL_ES2_BIT = 4;
    104 
    105         // TODO: Not currently used, keeping around until we know we don't need it
    106         @SuppressWarnings({"UnusedDeclaration"})
    107         private WallpaperObserver mReceiver;
    108 
    109         Bitmap mBackground;
    110         int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
    111         int mLastRotation = -1;
    112         float mXOffset;
    113         float mYOffset;
    114 
    115         boolean mVisible = true;
    116         boolean mRedrawNeeded;
    117         boolean mOffsetsChanged;
    118         int mLastXTranslation;
    119         int mLastYTranslation;
    120 
    121         private EGL10 mEgl;
    122         private EGLDisplay mEglDisplay;
    123         private EGLConfig mEglConfig;
    124         private EGLContext mEglContext;
    125         private EGLSurface mEglSurface;
    126 
    127         private static final String sSimpleVS =
    128                 "attribute vec4 position;\n" +
    129                 "attribute vec2 texCoords;\n" +
    130                 "varying vec2 outTexCoords;\n" +
    131                 "uniform mat4 projection;\n" +
    132                 "\nvoid main(void) {\n" +
    133                 "    outTexCoords = texCoords;\n" +
    134                 "    gl_Position = projection * position;\n" +
    135                 "}\n\n";
    136         private static final String sSimpleFS =
    137                 "precision mediump float;\n\n" +
    138                 "varying vec2 outTexCoords;\n" +
    139                 "uniform sampler2D texture;\n" +
    140                 "\nvoid main(void) {\n" +
    141                 "    gl_FragColor = texture2D(texture, outTexCoords);\n" +
    142                 "}\n\n";
    143 
    144         private static final int FLOAT_SIZE_BYTES = 4;
    145         private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
    146         private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
    147         private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
    148 
    149         class WallpaperObserver extends BroadcastReceiver {
    150             @Override
    151             public void onReceive(Context context, Intent intent) {
    152                 if (DEBUG) {
    153                     Log.d(TAG, "onReceive");
    154                 }
    155 
    156                 mLastSurfaceWidth = mLastSurfaceHeight = -1;
    157                 mBackground = null;
    158                 mRedrawNeeded = true;
    159                 drawFrame();
    160             }
    161         }
    162 
    163         public DrawableEngine() {
    164             super();
    165             setFixedSizeAllowed(true);
    166         }
    167 
    168         public void trimMemory(int level) {
    169             if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW &&
    170                     mBackground != null && mIsHwAccelerated) {
    171                 if (DEBUG) {
    172                     Log.d(TAG, "trimMemory");
    173                 }
    174                 mBackground.recycle();
    175                 mBackground = null;
    176                 mWallpaperManager.forgetLoadedWallpaper();
    177             }
    178         }
    179 
    180         @Override
    181         public void onCreate(SurfaceHolder surfaceHolder) {
    182             if (DEBUG) {
    183                 Log.d(TAG, "onCreate");
    184             }
    185 
    186             super.onCreate(surfaceHolder);
    187 
    188             // TODO: Don't need this currently because the wallpaper service
    189             // will restart the image wallpaper whenever the image changes.
    190             //IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
    191             //mReceiver = new WallpaperObserver();
    192             //registerReceiver(mReceiver, filter, null, mHandler);
    193 
    194             updateSurfaceSize(surfaceHolder);
    195 
    196             setOffsetNotificationsEnabled(false);
    197         }
    198 
    199         @Override
    200         public void onDestroy() {
    201             super.onDestroy();
    202             if (mReceiver != null) {
    203                 unregisterReceiver(mReceiver);
    204             }
    205         }
    206 
    207         @Override
    208         public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) {
    209             super.onDesiredSizeChanged(desiredWidth, desiredHeight);
    210             SurfaceHolder surfaceHolder = getSurfaceHolder();
    211             if (surfaceHolder != null) {
    212                 updateSurfaceSize(surfaceHolder);
    213             }
    214         }
    215 
    216         void updateSurfaceSize(SurfaceHolder surfaceHolder) {
    217             if (FIXED_SIZED_SURFACE) {
    218                 // Used a fixed size surface, because we are special.  We can do
    219                 // this because we know the current design of window animations doesn't
    220                 // cause this to break.
    221                 surfaceHolder.setFixedSize(getDesiredMinimumWidth(), getDesiredMinimumHeight());
    222             } else {
    223                 surfaceHolder.setSizeFromLayout();
    224             }
    225         }
    226 
    227         @Override
    228         public void onVisibilityChanged(boolean visible) {
    229             if (DEBUG) {
    230                 Log.d(TAG, "onVisibilityChanged: mVisible, visible=" + mVisible + ", " + visible);
    231             }
    232 
    233             if (mVisible != visible) {
    234                 if (DEBUG) {
    235                     Log.d(TAG, "Visibility changed to visible=" + visible);
    236                 }
    237                 mVisible = visible;
    238                 drawFrame();
    239             }
    240         }
    241 
    242         @Override
    243         public void onTouchEvent(MotionEvent event) {
    244             super.onTouchEvent(event);
    245         }
    246 
    247         @Override
    248         public void onOffsetsChanged(float xOffset, float yOffset,
    249                 float xOffsetStep, float yOffsetStep,
    250                 int xPixels, int yPixels) {
    251             if (DEBUG) {
    252                 Log.d(TAG, "onOffsetsChanged: xOffset=" + xOffset + ", yOffset=" + yOffset
    253                         + ", xOffsetStep=" + xOffsetStep + ", yOffsetStep=" + yOffsetStep
    254                         + ", xPixels=" + xPixels + ", yPixels=" + yPixels);
    255             }
    256 
    257             if (mXOffset != xOffset || mYOffset != yOffset) {
    258                 if (DEBUG) {
    259                     Log.d(TAG, "Offsets changed to (" + xOffset + "," + yOffset + ").");
    260                 }
    261                 mXOffset = xOffset;
    262                 mYOffset = yOffset;
    263                 mOffsetsChanged = true;
    264             }
    265             drawFrame();
    266         }
    267 
    268         @Override
    269         public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    270             if (DEBUG) {
    271                 Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height);
    272             }
    273 
    274             super.onSurfaceChanged(holder, format, width, height);
    275 
    276             drawFrame();
    277         }
    278 
    279         @Override
    280         public void onSurfaceDestroyed(SurfaceHolder holder) {
    281             super.onSurfaceDestroyed(holder);
    282             mLastSurfaceWidth = mLastSurfaceHeight = -1;
    283         }
    284 
    285         @Override
    286         public void onSurfaceCreated(SurfaceHolder holder) {
    287             super.onSurfaceCreated(holder);
    288             mLastSurfaceWidth = mLastSurfaceHeight = -1;
    289         }
    290 
    291         @Override
    292         public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
    293             if (DEBUG) {
    294                 Log.d(TAG, "onSurfaceRedrawNeeded");
    295             }
    296             super.onSurfaceRedrawNeeded(holder);
    297 
    298             drawFrame();
    299         }
    300 
    301         void drawFrame() {
    302             SurfaceHolder sh = getSurfaceHolder();
    303             final Rect frame = sh.getSurfaceFrame();
    304             final int dw = frame.width();
    305             final int dh = frame.height();
    306             int newRotation = ((WindowManager) getSystemService(WINDOW_SERVICE)).
    307                     getDefaultDisplay().getRotation();
    308             boolean surfaceDimensionsChanged = dw != mLastSurfaceWidth || dh != mLastSurfaceHeight;
    309 
    310             boolean redrawNeeded = surfaceDimensionsChanged || newRotation != mLastRotation;
    311             if (!redrawNeeded && !mOffsetsChanged) {
    312                 if (DEBUG) {
    313                     Log.d(TAG, "Suppressed drawFrame since redraw is not needed "
    314                             + "and offsets have not changed.");
    315                 }
    316                 return;
    317             }
    318             mLastRotation = newRotation;
    319 
    320             // Load bitmap if it is not yet loaded or if it was loaded at a different size
    321             if (mBackground == null || surfaceDimensionsChanged) {
    322                 if (DEBUG) {
    323                     Log.d(TAG, "Reloading bitmap: mBackground, bgw, bgh, dw, dh = " +
    324                             mBackground + ", " +
    325                             ((mBackground == null) ? 0 : mBackground.getWidth()) + ", " +
    326                             ((mBackground == null) ? 0 : mBackground.getHeight()) + ", " +
    327                             dw + ", " + dh);
    328                 }
    329                 mWallpaperManager.forgetLoadedWallpaper();
    330                 updateWallpaperLocked();
    331                 if (mBackground == null) {
    332                     if (DEBUG) {
    333                         Log.d(TAG, "Unable to load bitmap");
    334                     }
    335                     return;
    336                 }
    337                 if (DEBUG) {
    338                     if (dw != mBackground.getWidth() || dh != mBackground.getHeight()) {
    339                         Log.d(TAG, "Surface != bitmap dimensions: surface w/h, bitmap w/h: " +
    340                                 dw + ", " + dh + ", " + mBackground.getWidth() + ", " +
    341                                 mBackground.getHeight());
    342                     }
    343                 }
    344             }
    345 
    346             final int availw = dw - mBackground.getWidth();
    347             final int availh = dh - mBackground.getHeight();
    348             int xPixels = availw < 0 ? (int)(availw * mXOffset + .5f) : (availw / 2);
    349             int yPixels = availh < 0 ? (int)(availh * mYOffset + .5f) : (availh / 2);
    350 
    351             mOffsetsChanged = false;
    352             mRedrawNeeded = false;
    353             if (surfaceDimensionsChanged) {
    354                 mLastSurfaceWidth = dw;
    355                 mLastSurfaceHeight = dh;
    356             }
    357             mLastXTranslation = xPixels;
    358             mLastYTranslation = yPixels;
    359             if (!redrawNeeded && xPixels == mLastXTranslation && yPixels == mLastYTranslation) {
    360                 if (DEBUG) {
    361                     Log.d(TAG, "Suppressed drawFrame since the image has not "
    362                             + "actually moved an integral number of pixels.");
    363                 }
    364                 return;
    365             }
    366 
    367             if (DEBUG) {
    368                 Log.d(TAG, "Redrawing wallpaper");
    369             }
    370 
    371             if (mIsHwAccelerated) {
    372                 if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) {
    373                     drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
    374                 }
    375             } else {
    376                 drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
    377                 if (FIXED_SIZED_SURFACE) {
    378                     // If the surface is fixed-size, we should only need to
    379                     // draw it once and then we'll let the window manager
    380                     // position it appropriately.  As such, we no longer needed
    381                     // the loaded bitmap.  Yay!
    382                     // hw-accelerated path retains bitmap for faster rotation
    383                     mBackground = null;
    384                     mWallpaperManager.forgetLoadedWallpaper();
    385                 }
    386             }
    387 
    388         }
    389 
    390         private void updateWallpaperLocked() {
    391             Throwable exception = null;
    392             try {
    393                 mBackground = null;
    394                 mBackground = mWallpaperManager.getBitmap();
    395             } catch (RuntimeException e) {
    396                 exception = e;
    397             } catch (OutOfMemoryError e) {
    398                 exception = e;
    399             }
    400 
    401             if (exception != null) {
    402                 mBackground = null;
    403                 // Note that if we do fail at this, and the default wallpaper can't
    404                 // be loaded, we will go into a cycle.  Don't do a build where the
    405                 // default wallpaper can't be loaded.
    406                 Log.w(TAG, "Unable to load wallpaper!", exception);
    407                 try {
    408                     mWallpaperManager.clear();
    409                 } catch (IOException ex) {
    410                     // now we're really screwed.
    411                     Log.w(TAG, "Unable reset to default wallpaper!", ex);
    412                 }
    413             }
    414         }
    415 
    416         private void drawWallpaperWithCanvas(SurfaceHolder sh, int w, int h, int x, int y) {
    417             Canvas c = sh.lockCanvas();
    418             if (c != null) {
    419                 try {
    420                     if (DEBUG) {
    421                         Log.d(TAG, "Redrawing: x=" + x + ", y=" + y);
    422                     }
    423 
    424                     c.translate(x, y);
    425                     if (w < 0 || h < 0) {
    426                         c.save(Canvas.CLIP_SAVE_FLAG);
    427                         c.clipRect(0, 0, mBackground.getWidth(), mBackground.getHeight(),
    428                                 Op.DIFFERENCE);
    429                         c.drawColor(0xff000000);
    430                         c.restore();
    431                     }
    432                     if (mBackground != null) {
    433                         c.drawBitmap(mBackground, 0, 0, null);
    434                     }
    435                 } finally {
    436                     sh.unlockCanvasAndPost(c);
    437                 }
    438             }
    439         }
    440 
    441         private boolean drawWallpaperWithOpenGL(SurfaceHolder sh, int w, int h, int left, int top) {
    442             if (!initGL(sh)) return false;
    443 
    444             final float right = left + mBackground.getWidth();
    445             final float bottom = top + mBackground.getHeight();
    446 
    447             final Rect frame = sh.getSurfaceFrame();
    448             final Matrix4f ortho = new Matrix4f();
    449             ortho.loadOrtho(0.0f, frame.width(), frame.height(), 0.0f, -1.0f, 1.0f);
    450 
    451             final FloatBuffer triangleVertices = createMesh(left, top, right, bottom);
    452 
    453             final int texture = loadTexture(mBackground);
    454             final int program = buildProgram(sSimpleVS, sSimpleFS);
    455 
    456             final int attribPosition = glGetAttribLocation(program, "position");
    457             final int attribTexCoords = glGetAttribLocation(program, "texCoords");
    458             final int uniformTexture = glGetUniformLocation(program, "texture");
    459             final int uniformProjection = glGetUniformLocation(program, "projection");
    460 
    461             checkGlError();
    462 
    463             glViewport(0, 0, frame.width(), frame.height());
    464             glBindTexture(GL_TEXTURE_2D, texture);
    465 
    466             glUseProgram(program);
    467             glEnableVertexAttribArray(attribPosition);
    468             glEnableVertexAttribArray(attribTexCoords);
    469             glUniform1i(uniformTexture, 0);
    470             glUniformMatrix4fv(uniformProjection, 1, false, ortho.getArray(), 0);
    471 
    472             checkGlError();
    473 
    474             if (w < 0 || h < 0) {
    475                 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    476                 glClear(GL_COLOR_BUFFER_BIT);
    477             }
    478 
    479             // drawQuad
    480             triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
    481             glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false,
    482                     TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
    483 
    484             triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
    485             glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false,
    486                     TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
    487 
    488             glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    489 
    490             boolean status = mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
    491             checkEglError();
    492 
    493             finishGL();
    494 
    495             return status;
    496         }
    497 
    498         private FloatBuffer createMesh(int left, int top, float right, float bottom) {
    499             final float[] verticesData = {
    500                     // X, Y, Z, U, V
    501                      left,  bottom, 0.0f, 0.0f, 1.0f,
    502                      right, bottom, 0.0f, 1.0f, 1.0f,
    503                      left,  top,    0.0f, 0.0f, 0.0f,
    504                      right, top,    0.0f, 1.0f, 0.0f,
    505             };
    506 
    507             final int bytes = verticesData.length * FLOAT_SIZE_BYTES;
    508             final FloatBuffer triangleVertices = ByteBuffer.allocateDirect(bytes).order(
    509                     ByteOrder.nativeOrder()).asFloatBuffer();
    510             triangleVertices.put(verticesData).position(0);
    511             return triangleVertices;
    512         }
    513 
    514         private int loadTexture(Bitmap bitmap) {
    515             int[] textures = new int[1];
    516 
    517             glActiveTexture(GL_TEXTURE0);
    518             glGenTextures(1, textures, 0);
    519             checkGlError();
    520 
    521             int texture = textures[0];
    522             glBindTexture(GL_TEXTURE_2D, texture);
    523             checkGlError();
    524 
    525             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    526             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    527 
    528             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    529             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    530 
    531             GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0);
    532             checkGlError();
    533 
    534             return texture;
    535         }
    536 
    537         private int buildProgram(String vertex, String fragment) {
    538             int vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
    539             if (vertexShader == 0) return 0;
    540 
    541             int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
    542             if (fragmentShader == 0) return 0;
    543 
    544             int program = glCreateProgram();
    545             glAttachShader(program, vertexShader);
    546             checkGlError();
    547 
    548             glAttachShader(program, fragmentShader);
    549             checkGlError();
    550 
    551             glLinkProgram(program);
    552             checkGlError();
    553 
    554             int[] status = new int[1];
    555             glGetProgramiv(program, GL_LINK_STATUS, status, 0);
    556             if (status[0] != GL_TRUE) {
    557                 String error = glGetProgramInfoLog(program);
    558                 Log.d(GL_LOG_TAG, "Error while linking program:\n" + error);
    559                 glDeleteShader(vertexShader);
    560                 glDeleteShader(fragmentShader);
    561                 glDeleteProgram(program);
    562                 return 0;
    563             }
    564 
    565             return program;
    566         }
    567 
    568         private int buildShader(String source, int type) {
    569             int shader = glCreateShader(type);
    570 
    571             glShaderSource(shader, source);
    572             checkGlError();
    573 
    574             glCompileShader(shader);
    575             checkGlError();
    576 
    577             int[] status = new int[1];
    578             glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);
    579             if (status[0] != GL_TRUE) {
    580                 String error = glGetShaderInfoLog(shader);
    581                 Log.d(GL_LOG_TAG, "Error while compiling shader:\n" + error);
    582                 glDeleteShader(shader);
    583                 return 0;
    584             }
    585 
    586             return shader;
    587         }
    588 
    589         private void checkEglError() {
    590             int error = mEgl.eglGetError();
    591             if (error != EGL_SUCCESS) {
    592                 Log.w(GL_LOG_TAG, "EGL error = " + GLUtils.getEGLErrorString(error));
    593             }
    594         }
    595 
    596         private void checkGlError() {
    597             int error = glGetError();
    598             if (error != GL_NO_ERROR) {
    599                 Log.w(GL_LOG_TAG, "GL error = 0x" + Integer.toHexString(error), new Throwable());
    600             }
    601         }
    602 
    603         private void finishGL() {
    604             mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    605             mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
    606             mEgl.eglDestroyContext(mEglDisplay, mEglContext);
    607             mEgl.eglTerminate(mEglDisplay);
    608         }
    609 
    610         private boolean initGL(SurfaceHolder surfaceHolder) {
    611             mEgl = (EGL10) EGLContext.getEGL();
    612 
    613             mEglDisplay = mEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
    614             if (mEglDisplay == EGL_NO_DISPLAY) {
    615                 throw new RuntimeException("eglGetDisplay failed " +
    616                         GLUtils.getEGLErrorString(mEgl.eglGetError()));
    617             }
    618 
    619             int[] version = new int[2];
    620             if (!mEgl.eglInitialize(mEglDisplay, version)) {
    621                 throw new RuntimeException("eglInitialize failed " +
    622                         GLUtils.getEGLErrorString(mEgl.eglGetError()));
    623             }
    624 
    625             mEglConfig = chooseEglConfig();
    626             if (mEglConfig == null) {
    627                 throw new RuntimeException("eglConfig not initialized");
    628             }
    629 
    630             mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
    631             if (mEglContext == EGL_NO_CONTEXT) {
    632                 throw new RuntimeException("createContext failed " +
    633                         GLUtils.getEGLErrorString(mEgl.eglGetError()));
    634             }
    635 
    636             int attribs[] = {
    637                 EGL_WIDTH, 1,
    638                 EGL_HEIGHT, 1,
    639                 EGL_NONE
    640             };
    641             EGLSurface tmpSurface = mEgl.eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
    642             mEgl.eglMakeCurrent(mEglDisplay, tmpSurface, tmpSurface, mEglContext);
    643 
    644             int[] maxSize = new int[1];
    645             Rect frame = surfaceHolder.getSurfaceFrame();
    646             glGetIntegerv(GL_MAX_TEXTURE_SIZE, maxSize, 0);
    647 
    648             mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    649             mEgl.eglDestroySurface(mEglDisplay, tmpSurface);
    650 
    651             if(frame.width() > maxSize[0] || frame.height() > maxSize[0]) {
    652                 mEgl.eglDestroyContext(mEglDisplay, mEglContext);
    653                 mEgl.eglTerminate(mEglDisplay);
    654                 Log.e(GL_LOG_TAG, "requested  texture size " +
    655                     frame.width() + "x" + frame.height() + " exceeds the support maximum of " +
    656                     maxSize[0] + "x" + maxSize[0]);
    657                 return false;
    658             }
    659 
    660             mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null);
    661             if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
    662                 int error = mEgl.eglGetError();
    663                 if (error == EGL_BAD_NATIVE_WINDOW || error == EGL_BAD_ALLOC) {
    664                     Log.e(GL_LOG_TAG, "createWindowSurface returned " +
    665                                          GLUtils.getEGLErrorString(error) + ".");
    666                     return false;
    667                 }
    668                 throw new RuntimeException("createWindowSurface failed " +
    669                         GLUtils.getEGLErrorString(error));
    670             }
    671 
    672             if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
    673                 throw new RuntimeException("eglMakeCurrent failed " +
    674                         GLUtils.getEGLErrorString(mEgl.eglGetError()));
    675             }
    676 
    677             return true;
    678         }
    679 
    680 
    681         EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
    682             int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
    683             return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attrib_list);
    684         }
    685 
    686         private EGLConfig chooseEglConfig() {
    687             int[] configsCount = new int[1];
    688             EGLConfig[] configs = new EGLConfig[1];
    689             int[] configSpec = getConfig();
    690             if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
    691                 throw new IllegalArgumentException("eglChooseConfig failed " +
    692                         GLUtils.getEGLErrorString(mEgl.eglGetError()));
    693             } else if (configsCount[0] > 0) {
    694                 return configs[0];
    695             }
    696             return null;
    697         }
    698 
    699         private int[] getConfig() {
    700             return new int[] {
    701                     EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
    702                     EGL_RED_SIZE, 8,
    703                     EGL_GREEN_SIZE, 8,
    704                     EGL_BLUE_SIZE, 8,
    705                     EGL_ALPHA_SIZE, 0,
    706                     EGL_DEPTH_SIZE, 0,
    707                     EGL_STENCIL_SIZE, 0,
    708                     EGL_CONFIG_CAVEAT, EGL_NONE,
    709                     EGL_NONE
    710             };
    711         }
    712     }
    713 }
    714