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