OSDN Git Service

ImageWallpaper: don't throw-up when EGL init fails
[android-x86/frameworks-base.git] / packages / SystemUI / src / com / android / systemui / ImageWallpaper.java
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
21 import static javax.microedition.khronos.egl.EGL10.*;
22
23 import android.app.ActivityManager;
24 import android.app.WallpaperManager;
25 import android.content.ComponentCallbacks2;
26 import android.graphics.Bitmap;
27 import android.graphics.Canvas;
28 import android.graphics.Rect;
29 import android.graphics.RectF;
30 import android.graphics.Region.Op;
31 import android.opengl.GLUtils;
32 import android.os.AsyncTask;
33 import android.os.SystemProperties;
34 import android.os.Trace;
35 import android.renderscript.Matrix4f;
36 import android.service.wallpaper.WallpaperService;
37 import android.util.Log;
38 import android.view.Display;
39 import android.view.DisplayInfo;
40 import android.view.MotionEvent;
41 import android.view.SurfaceHolder;
42 import android.view.WindowManager;
43
44 import java.io.FileDescriptor;
45 import java.io.IOException;
46 import java.io.PrintWriter;
47 import java.nio.ByteBuffer;
48 import java.nio.ByteOrder;
49 import java.nio.FloatBuffer;
50
51 import javax.microedition.khronos.egl.EGL10;
52 import javax.microedition.khronos.egl.EGLConfig;
53 import javax.microedition.khronos.egl.EGLContext;
54 import javax.microedition.khronos.egl.EGLDisplay;
55 import javax.microedition.khronos.egl.EGLSurface;
56
57 /**
58  * Default built-in wallpaper that simply shows a static image.
59  */
60 @SuppressWarnings({"UnusedDeclaration"})
61 public class ImageWallpaper extends WallpaperService {
62     private static final String TAG = "ImageWallpaper";
63     private static final String GL_LOG_TAG = "ImageWallpaperGL";
64     private static final boolean DEBUG = false;
65     private static final String PROPERTY_KERNEL_QEMU = "ro.kernel.qemu";
66
67     static final boolean FIXED_SIZED_SURFACE = true;
68     static final boolean USE_OPENGL = true;
69
70     WallpaperManager mWallpaperManager;
71
72     DrawableEngine mEngine;
73
74     boolean mIsHwAccelerated;
75
76     @Override
77     public void onCreate() {
78         super.onCreate();
79         mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE);
80
81         //noinspection PointlessBooleanExpression,ConstantConditions
82         if (FIXED_SIZED_SURFACE && USE_OPENGL) {
83             mIsHwAccelerated = ActivityManager.isHighEndGfx();
84         }
85     }
86
87     @Override
88     public void onTrimMemory(int level) {
89         if (mEngine != null) {
90             mEngine.trimMemory(level);
91         }
92     }
93
94     @Override
95     public Engine onCreateEngine() {
96         mEngine = new DrawableEngine();
97         return mEngine;
98     }
99
100     class DrawableEngine extends Engine {
101         static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
102         static final int EGL_OPENGL_ES2_BIT = 4;
103
104         Bitmap mBackground;
105         int mBackgroundWidth = -1, mBackgroundHeight = -1;
106         int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
107         int mLastRotation = -1;
108         float mXOffset = 0.5f;
109         float mYOffset = 0.5f;
110         float mScale = 1f;
111
112         private Display mDefaultDisplay;
113         private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
114
115         boolean mVisible = true;
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
126         private static final String sSimpleVS =
127                 "attribute vec4 position;\n" +
128                 "attribute vec2 texCoords;\n" +
129                 "varying vec2 outTexCoords;\n" +
130                 "uniform mat4 projection;\n" +
131                 "\nvoid main(void) {\n" +
132                 "    outTexCoords = texCoords;\n" +
133                 "    gl_Position = projection * position;\n" +
134                 "}\n\n";
135         private static final String sSimpleFS =
136                 "precision mediump float;\n\n" +
137                 "varying vec2 outTexCoords;\n" +
138                 "uniform sampler2D texture;\n" +
139                 "\nvoid main(void) {\n" +
140                 "    gl_FragColor = texture2D(texture, outTexCoords);\n" +
141                 "}\n\n";
142
143         private static final int FLOAT_SIZE_BYTES = 4;
144         private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
145         private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
146         private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
147
148         private int mRotationAtLastSurfaceSizeUpdate = -1;
149         private int mDisplayWidthAtLastSurfaceSizeUpdate = -1;
150         private int mDisplayHeightAtLastSurfaceSizeUpdate = -1;
151
152         private int mLastRequestedWidth = -1;
153         private int mLastRequestedHeight = -1;
154         private AsyncTask<Void, Void, Bitmap> mLoader;
155         private boolean mNeedsDrawAfterLoadingWallpaper;
156         private boolean mSurfaceValid;
157
158         public DrawableEngine() {
159             super();
160             setFixedSizeAllowed(true);
161         }
162
163         public void trimMemory(int level) {
164             if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW
165                     && level <= ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL
166                     && mBackground != null) {
167                 if (DEBUG) {
168                     Log.d(TAG, "trimMemory");
169                 }
170                 unloadWallpaper(true /* forgetSize */);
171             }
172         }
173
174         @Override
175         public void onCreate(SurfaceHolder surfaceHolder) {
176             if (DEBUG) {
177                 Log.d(TAG, "onCreate");
178             }
179
180             super.onCreate(surfaceHolder);
181
182             mDefaultDisplay = getSystemService(WindowManager.class).getDefaultDisplay();
183             setOffsetNotificationsEnabled(false);
184
185             updateSurfaceSize(surfaceHolder, getDefaultDisplayInfo(), false /* forDraw */);
186         }
187
188         @Override
189         public void onDestroy() {
190             super.onDestroy();
191             mBackground = null;
192             unloadWallpaper(true /* forgetSize */);
193         }
194
195         boolean updateSurfaceSize(SurfaceHolder surfaceHolder, DisplayInfo displayInfo,
196                 boolean forDraw) {
197             boolean hasWallpaper = true;
198
199             // Load background image dimensions, if we haven't saved them yet
200             if (mBackgroundWidth <= 0 || mBackgroundHeight <= 0) {
201                 // Need to load the image to get dimensions
202                 loadWallpaper(forDraw, false /* needsReset */);
203                 if (DEBUG) {
204                     Log.d(TAG, "Reloading, redoing updateSurfaceSize later.");
205                 }
206                 hasWallpaper = false;
207             }
208
209             // Force the wallpaper to cover the screen in both dimensions
210             int surfaceWidth = Math.max(displayInfo.logicalWidth, mBackgroundWidth);
211             int surfaceHeight = Math.max(displayInfo.logicalHeight, mBackgroundHeight);
212
213             if (FIXED_SIZED_SURFACE) {
214                 // Used a fixed size surface, because we are special.  We can do
215                 // this because we know the current design of window animations doesn't
216                 // cause this to break.
217                 surfaceHolder.setFixedSize(surfaceWidth, surfaceHeight);
218                 mLastRequestedWidth = surfaceWidth;
219                 mLastRequestedHeight = surfaceHeight;
220             } else {
221                 surfaceHolder.setSizeFromLayout();
222             }
223             return hasWallpaper;
224         }
225
226         @Override
227         public void onVisibilityChanged(boolean visible) {
228             if (DEBUG) {
229                 Log.d(TAG, "onVisibilityChanged: mVisible, visible=" + mVisible + ", " + visible);
230             }
231
232             if (mVisible != visible) {
233                 if (DEBUG) {
234                     Log.d(TAG, "Visibility changed to visible=" + visible);
235                 }
236                 mVisible = visible;
237                 if (visible) {
238                     drawFrame();
239                 }
240             }
241         }
242
243         @Override
244         public void onOffsetsChanged(float xOffset, float yOffset,
245                 float xOffsetStep, float yOffsetStep,
246                 int xPixels, int yPixels) {
247             if (DEBUG) {
248                 Log.d(TAG, "onOffsetsChanged: xOffset=" + xOffset + ", yOffset=" + yOffset
249                         + ", xOffsetStep=" + xOffsetStep + ", yOffsetStep=" + yOffsetStep
250                         + ", xPixels=" + xPixels + ", yPixels=" + yPixels);
251             }
252
253             if (mXOffset != xOffset || mYOffset != yOffset) {
254                 if (DEBUG) {
255                     Log.d(TAG, "Offsets changed to (" + xOffset + "," + yOffset + ").");
256                 }
257                 mXOffset = xOffset;
258                 mYOffset = yOffset;
259                 mOffsetsChanged = true;
260             }
261             drawFrame();
262         }
263
264         @Override
265         public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
266             if (DEBUG) {
267                 Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height);
268             }
269
270             super.onSurfaceChanged(holder, format, width, height);
271
272             drawFrame();
273         }
274
275         @Override
276         public void onSurfaceDestroyed(SurfaceHolder holder) {
277             super.onSurfaceDestroyed(holder);
278             if (DEBUG) {
279                 Log.i(TAG, "onSurfaceDestroyed");
280             }
281
282             mLastSurfaceWidth = mLastSurfaceHeight = -1;
283             mSurfaceValid = false;
284         }
285
286         @Override
287         public void onSurfaceCreated(SurfaceHolder holder) {
288             super.onSurfaceCreated(holder);
289             if (DEBUG) {
290                 Log.i(TAG, "onSurfaceCreated");
291             }
292
293             mLastSurfaceWidth = mLastSurfaceHeight = -1;
294             mSurfaceValid = true;
295         }
296
297         @Override
298         public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
299             if (DEBUG) {
300                 Log.d(TAG, "onSurfaceRedrawNeeded");
301             }
302             super.onSurfaceRedrawNeeded(holder);
303             drawFrame();
304         }
305
306         private DisplayInfo getDefaultDisplayInfo() {
307             mDefaultDisplay.getDisplayInfo(mTmpDisplayInfo);
308             return mTmpDisplayInfo;
309         }
310
311         void drawFrame() {
312             if (!mSurfaceValid) {
313                 return;
314             }
315             try {
316                 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "drawWallpaper");
317                 DisplayInfo displayInfo = getDefaultDisplayInfo();
318                 int newRotation = displayInfo.rotation;
319
320                 // Sometimes a wallpaper is not large enough to cover the screen in one dimension.
321                 // Call updateSurfaceSize -- it will only actually do the update if the dimensions
322                 // should change
323                 if (newRotation != mLastRotation) {
324                     // Update surface size (if necessary)
325                     if (!updateSurfaceSize(getSurfaceHolder(), displayInfo, true /* forDraw */)) {
326                         return; // had to reload wallpaper, will retry later
327                     }
328                     mRotationAtLastSurfaceSizeUpdate = newRotation;
329                     mDisplayWidthAtLastSurfaceSizeUpdate = displayInfo.logicalWidth;
330                     mDisplayHeightAtLastSurfaceSizeUpdate = displayInfo.logicalHeight;
331                 }
332                 SurfaceHolder sh = getSurfaceHolder();
333                 final Rect frame = sh.getSurfaceFrame();
334                 final int dw = frame.width();
335                 final int dh = frame.height();
336                 boolean surfaceDimensionsChanged = dw != mLastSurfaceWidth
337                         || dh != mLastSurfaceHeight;
338
339                 boolean redrawNeeded = surfaceDimensionsChanged || newRotation != mLastRotation;
340                 if (!redrawNeeded && !mOffsetsChanged) {
341                     if (DEBUG) {
342                         Log.d(TAG, "Suppressed drawFrame since redraw is not needed "
343                                 + "and offsets have not changed.");
344                     }
345                     return;
346                 }
347                 mLastRotation = newRotation;
348
349                 // Load bitmap if it is not yet loaded
350                 if (mBackground == null) {
351                     if (DEBUG) {
352                         Log.d(TAG, "Reloading bitmap: mBackground, bgw, bgh, dw, dh = " +
353                                 mBackground + ", " +
354                                 ((mBackground == null) ? 0 : mBackground.getWidth()) + ", " +
355                                 ((mBackground == null) ? 0 : mBackground.getHeight()) + ", " +
356                                 dw + ", " + dh);
357                     }
358                     loadWallpaper(true /* needDraw */, true /* needReset */);
359                     if (DEBUG) {
360                         Log.d(TAG, "Reloading, resuming draw later");
361                     }
362                     return;
363                 }
364
365                 // Center the scaled image
366                 mScale = Math.max(1f, Math.max(dw / (float) mBackground.getWidth(),
367                         dh / (float) mBackground.getHeight()));
368                 final int availw = dw - (int) (mBackground.getWidth() * mScale);
369                 final int availh = dh - (int) (mBackground.getHeight() * mScale);
370                 int xPixels = availw / 2;
371                 int yPixels = availh / 2;
372
373                 // Adjust the image for xOffset/yOffset values. If window manager is handling offsets,
374                 // mXOffset and mYOffset are set to 0.5f by default and therefore xPixels and yPixels
375                 // will remain unchanged
376                 final int availwUnscaled = dw - mBackground.getWidth();
377                 final int availhUnscaled = dh - mBackground.getHeight();
378                 if (availwUnscaled < 0)
379                     xPixels += (int) (availwUnscaled * (mXOffset - .5f) + .5f);
380                 if (availhUnscaled < 0)
381                     yPixels += (int) (availhUnscaled * (mYOffset - .5f) + .5f);
382
383                 mOffsetsChanged = false;
384                 if (surfaceDimensionsChanged) {
385                     mLastSurfaceWidth = dw;
386                     mLastSurfaceHeight = dh;
387                 }
388                 if (!redrawNeeded && xPixels == mLastXTranslation && yPixels == mLastYTranslation) {
389                     if (DEBUG) {
390                         Log.d(TAG, "Suppressed drawFrame since the image has not "
391                                 + "actually moved an integral number of pixels.");
392                     }
393                     return;
394                 }
395                 mLastXTranslation = xPixels;
396                 mLastYTranslation = yPixels;
397
398                 if (DEBUG) {
399                     Log.d(TAG, "Redrawing wallpaper");
400                 }
401
402                 if (mIsHwAccelerated) {
403                     if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) {
404                         drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
405                     }
406                 } else {
407                     drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
408                     if (FIXED_SIZED_SURFACE) {
409                         // If the surface is fixed-size, we should only need to
410                         // draw it once and then we'll let the window manager
411                         // position it appropriately.  As such, we no longer needed
412                         // the loaded bitmap.  Yay!
413                         // hw-accelerated renderer retains bitmap for faster rotation
414                         unloadWallpaper(false /* forgetSize */);
415                     }
416                 }
417             } finally {
418                 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
419             }
420         }
421
422         /**
423          * Loads the wallpaper on background thread and schedules updating the surface frame,
424          * and if {@param needsDraw} is set also draws a frame.
425          *
426          * If loading is already in-flight, subsequent loads are ignored (but needDraw is or-ed to
427          * the active request).
428          *
429          * If {@param needsReset} is set also clears the cache in WallpaperManager first.
430          */
431         private void loadWallpaper(boolean needsDraw, boolean needsReset) {
432             mNeedsDrawAfterLoadingWallpaper |= needsDraw;
433             if (mLoader != null) {
434                 if (needsReset) {
435                     mLoader.cancel(false /* interrupt */);
436                     mLoader = null;
437                 } else {
438                     if (DEBUG) {
439                         Log.d(TAG, "Skipping loadWallpaper, already in flight ");
440                     }
441                     return;
442                 }
443             }
444             mLoader = new AsyncTask<Void, Void, Bitmap>() {
445                 @Override
446                 protected Bitmap doInBackground(Void... params) {
447                     Throwable exception;
448                     try {
449                         if (needsReset) {
450                             mWallpaperManager.forgetLoadedWallpaper();
451                         }
452                         return mWallpaperManager.getBitmap();
453                     } catch (RuntimeException | OutOfMemoryError e) {
454                         exception = e;
455                     }
456
457                     if (isCancelled()) {
458                         return null;
459                     }
460
461                     if (exception != null) {
462                         // Note that if we do fail at this, and the default wallpaper can't
463                         // be loaded, we will go into a cycle.  Don't do a build where the
464                         // default wallpaper can't be loaded.
465                         Log.w(TAG, "Unable to load wallpaper!", exception);
466                         try {
467                             mWallpaperManager.clear();
468                         } catch (IOException ex) {
469                             // now we're really screwed.
470                             Log.w(TAG, "Unable reset to default wallpaper!", ex);
471                         }
472
473                         if (isCancelled()) {
474                             return null;
475                         }
476
477                         try {
478                             return mWallpaperManager.getBitmap();
479                         } catch (RuntimeException | OutOfMemoryError e) {
480                             Log.w(TAG, "Unable to load default wallpaper!", e);
481                         }
482                     }
483                     return null;
484                 }
485
486                 @Override
487                 protected void onPostExecute(Bitmap b) {
488                     mBackground = null;
489                     mBackgroundWidth = -1;
490                     mBackgroundHeight = -1;
491
492                     if (b != null) {
493                         mBackground = b;
494                         mBackgroundWidth = mBackground.getWidth();
495                         mBackgroundHeight = mBackground.getHeight();
496                     }
497
498                     if (DEBUG) {
499                         Log.d(TAG, "Wallpaper loaded: " + mBackground);
500                     }
501                     updateSurfaceSize(getSurfaceHolder(), getDefaultDisplayInfo(),
502                             false /* forDraw */);
503                     if (mNeedsDrawAfterLoadingWallpaper) {
504                         drawFrame();
505                     }
506
507                     mLoader = null;
508                     mNeedsDrawAfterLoadingWallpaper = false;
509                 }
510             }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
511         }
512
513         private void unloadWallpaper(boolean forgetSize) {
514             if (mLoader != null) {
515                 mLoader.cancel(false);
516                 mLoader = null;
517             }
518             mBackground = null;
519             if (forgetSize) {
520                 mBackgroundWidth = -1;
521                 mBackgroundHeight = -1;
522             }
523
524             mLoader = new AsyncTask<Void, Void, Bitmap>() {
525                 @Override
526                 protected Bitmap doInBackground(Void... params) {
527                     mWallpaperManager.forgetLoadedWallpaper();
528                     return null;
529                 }
530             }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
531         }
532
533         @Override
534         protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
535             super.dump(prefix, fd, out, args);
536
537             out.print(prefix); out.println("ImageWallpaper.DrawableEngine:");
538             out.print(prefix); out.print(" mBackground="); out.print(mBackground);
539             out.print(" mBackgroundWidth="); out.print(mBackgroundWidth);
540             out.print(" mBackgroundHeight="); out.println(mBackgroundHeight);
541
542             out.print(prefix); out.print(" mLastRotation="); out.print(mLastRotation);
543             out.print(" mLastSurfaceWidth="); out.print(mLastSurfaceWidth);
544             out.print(" mLastSurfaceHeight="); out.println(mLastSurfaceHeight);
545
546             out.print(prefix); out.print(" mXOffset="); out.print(mXOffset);
547             out.print(" mYOffset="); out.println(mYOffset);
548
549             out.print(prefix); out.print(" mVisible="); out.print(mVisible);
550             out.print(" mOffsetsChanged="); out.println(mOffsetsChanged);
551
552             out.print(prefix); out.print(" mLastXTranslation="); out.print(mLastXTranslation);
553             out.print(" mLastYTranslation="); out.print(mLastYTranslation);
554             out.print(" mScale="); out.println(mScale);
555
556             out.print(prefix); out.print(" mLastRequestedWidth="); out.print(mLastRequestedWidth);
557             out.print(" mLastRequestedHeight="); out.println(mLastRequestedHeight);
558
559             out.print(prefix); out.println(" DisplayInfo at last updateSurfaceSize:");
560             out.print(prefix);
561             out.print("  rotation="); out.print(mRotationAtLastSurfaceSizeUpdate);
562             out.print("  width="); out.print(mDisplayWidthAtLastSurfaceSizeUpdate);
563             out.print("  height="); out.println(mDisplayHeightAtLastSurfaceSizeUpdate);
564         }
565
566         private void drawWallpaperWithCanvas(SurfaceHolder sh, int w, int h, int left, int top) {
567             Canvas c = sh.lockCanvas();
568             if (c != null) {
569                 try {
570                     if (DEBUG) {
571                         Log.d(TAG, "Redrawing: left=" + left + ", top=" + top);
572                     }
573
574                     final float right = left + mBackground.getWidth() * mScale;
575                     final float bottom = top + mBackground.getHeight() * mScale;
576                     if (w < 0 || h < 0) {
577                         c.save(Canvas.CLIP_SAVE_FLAG);
578                         c.clipRect(left, top, right, bottom,
579                                 Op.DIFFERENCE);
580                         c.drawColor(0xff000000);
581                         c.restore();
582                     }
583                     if (mBackground != null) {
584                         RectF dest = new RectF(left, top, right, bottom);
585                         // add a filter bitmap?
586                         c.drawBitmap(mBackground, null, dest, null);
587                     }
588                 } finally {
589                     sh.unlockCanvasAndPost(c);
590                 }
591             }
592         }
593
594         private boolean drawWallpaperWithOpenGL(SurfaceHolder sh, int w, int h, int left, int top) {
595             if (!initGL(sh)) return false;
596
597             final float right = left + mBackground.getWidth() * mScale;
598             final float bottom = top + mBackground.getHeight() * mScale;
599
600             final Rect frame = sh.getSurfaceFrame();
601             final Matrix4f ortho = new Matrix4f();
602             ortho.loadOrtho(0.0f, frame.width(), frame.height(), 0.0f, -1.0f, 1.0f);
603
604             final FloatBuffer triangleVertices = createMesh(left, top, right, bottom);
605
606             final int texture = loadTexture(mBackground);
607             final int program = buildProgram(sSimpleVS, sSimpleFS);
608
609             final int attribPosition = glGetAttribLocation(program, "position");
610             final int attribTexCoords = glGetAttribLocation(program, "texCoords");
611             final int uniformTexture = glGetUniformLocation(program, "texture");
612             final int uniformProjection = glGetUniformLocation(program, "projection");
613
614             checkGlError();
615
616             glViewport(0, 0, frame.width(), frame.height());
617             glBindTexture(GL_TEXTURE_2D, texture);
618
619             glUseProgram(program);
620             glEnableVertexAttribArray(attribPosition);
621             glEnableVertexAttribArray(attribTexCoords);
622             glUniform1i(uniformTexture, 0);
623             glUniformMatrix4fv(uniformProjection, 1, false, ortho.getArray(), 0);
624
625             checkGlError();
626
627             if (w > 0 || h > 0) {
628                 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
629                 glClear(GL_COLOR_BUFFER_BIT);
630             }
631
632             // drawQuad
633             triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
634             glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false,
635                     TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
636
637             triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
638             glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false,
639                     TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
640
641             glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
642
643             boolean status = mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
644             checkEglError();
645
646             finishGL(texture, program);
647
648             return status;
649         }
650
651         private FloatBuffer createMesh(int left, int top, float right, float bottom) {
652             final float[] verticesData = {
653                     // X, Y, Z, U, V
654                      left,  bottom, 0.0f, 0.0f, 1.0f,
655                      right, bottom, 0.0f, 1.0f, 1.0f,
656                      left,  top,    0.0f, 0.0f, 0.0f,
657                      right, top,    0.0f, 1.0f, 0.0f,
658             };
659
660             final int bytes = verticesData.length * FLOAT_SIZE_BYTES;
661             final FloatBuffer triangleVertices = ByteBuffer.allocateDirect(bytes).order(
662                     ByteOrder.nativeOrder()).asFloatBuffer();
663             triangleVertices.put(verticesData).position(0);
664             return triangleVertices;
665         }
666
667         private int loadTexture(Bitmap bitmap) {
668             int[] textures = new int[1];
669
670             glActiveTexture(GL_TEXTURE0);
671             glGenTextures(1, textures, 0);
672             checkGlError();
673
674             int texture = textures[0];
675             glBindTexture(GL_TEXTURE_2D, texture);
676             checkGlError();
677
678             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
679             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
680
681             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
682             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
683
684             GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0);
685             checkGlError();
686
687             return texture;
688         }
689
690         private int buildProgram(String vertex, String fragment) {
691             int vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
692             if (vertexShader == 0) return 0;
693
694             int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
695             if (fragmentShader == 0) return 0;
696
697             int program = glCreateProgram();
698             glAttachShader(program, vertexShader);
699             glAttachShader(program, fragmentShader);
700             glLinkProgram(program);
701             checkGlError();
702
703             glDeleteShader(vertexShader);
704             glDeleteShader(fragmentShader);
705
706             int[] status = new int[1];
707             glGetProgramiv(program, GL_LINK_STATUS, status, 0);
708             if (status[0] != GL_TRUE) {
709                 String error = glGetProgramInfoLog(program);
710                 Log.d(GL_LOG_TAG, "Error while linking program:\n" + error);
711                 glDeleteProgram(program);
712                 return 0;
713             }
714
715             return program;
716         }
717
718         private int buildShader(String source, int type) {
719             int shader = glCreateShader(type);
720
721             glShaderSource(shader, source);
722             checkGlError();
723
724             glCompileShader(shader);
725             checkGlError();
726
727             int[] status = new int[1];
728             glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);
729             if (status[0] != GL_TRUE) {
730                 String error = glGetShaderInfoLog(shader);
731                 Log.d(GL_LOG_TAG, "Error while compiling shader:\n" + error);
732                 glDeleteShader(shader);
733                 return 0;
734             }
735
736             return shader;
737         }
738
739         private void checkEglError() {
740             int error = mEgl.eglGetError();
741             if (error != EGL_SUCCESS) {
742                 Log.w(GL_LOG_TAG, "EGL error = " + GLUtils.getEGLErrorString(error));
743             }
744         }
745
746         private void checkGlError() {
747             int error = glGetError();
748             if (error != GL_NO_ERROR) {
749                 Log.w(GL_LOG_TAG, "GL error = 0x" + Integer.toHexString(error), new Throwable());
750             }
751         }
752
753         private void finishGL(int texture, int program) {
754             int[] textures = new int[1];
755             textures[0] = texture;
756             glDeleteTextures(1, textures, 0);
757             glDeleteProgram(program);
758             mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
759             mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
760             mEgl.eglDestroyContext(mEglDisplay, mEglContext);
761             mEgl.eglTerminate(mEglDisplay);
762         }
763
764         private boolean initGL(SurfaceHolder surfaceHolder) {
765             mEgl = (EGL10) EGLContext.getEGL();
766
767             mEglDisplay = mEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
768             if (mEglDisplay == EGL_NO_DISPLAY) {
769                 throw new RuntimeException("eglGetDisplay failed " +
770                         GLUtils.getEGLErrorString(mEgl.eglGetError()));
771             }
772
773             int[] version = new int[2];
774             if (!mEgl.eglInitialize(mEglDisplay, version)) {
775                 throw new RuntimeException("eglInitialize failed " +
776                         GLUtils.getEGLErrorString(mEgl.eglGetError()));
777             }
778
779             mEglConfig = chooseEglConfig();
780             if (mEglConfig == null) {
781                 return false;
782             }
783
784             mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
785             if (mEglContext == EGL_NO_CONTEXT) {
786                 throw new RuntimeException("createContext failed " +
787                         GLUtils.getEGLErrorString(mEgl.eglGetError()));
788             }
789
790             int attribs[] = {
791                 EGL_WIDTH, 1,
792                 EGL_HEIGHT, 1,
793                 EGL_NONE
794             };
795             EGLSurface tmpSurface = mEgl.eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
796             mEgl.eglMakeCurrent(mEglDisplay, tmpSurface, tmpSurface, mEglContext);
797
798             int[] maxSize = new int[1];
799             Rect frame = surfaceHolder.getSurfaceFrame();
800             glGetIntegerv(GL_MAX_TEXTURE_SIZE, maxSize, 0);
801
802             mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
803             mEgl.eglDestroySurface(mEglDisplay, tmpSurface);
804
805             if(frame.width() > maxSize[0] || frame.height() > maxSize[0]) {
806                 mEgl.eglDestroyContext(mEglDisplay, mEglContext);
807                 mEgl.eglTerminate(mEglDisplay);
808                 Log.e(GL_LOG_TAG, "requested  texture size " +
809                     frame.width() + "x" + frame.height() + " exceeds the support maximum of " +
810                     maxSize[0] + "x" + maxSize[0]);
811                 return false;
812             }
813
814             mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null);
815             if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
816                 int error = mEgl.eglGetError();
817                 if (error == EGL_BAD_NATIVE_WINDOW || error == EGL_BAD_ALLOC) {
818                     Log.e(GL_LOG_TAG, "createWindowSurface returned " +
819                                          GLUtils.getEGLErrorString(error) + ".");
820                     return false;
821                 }
822                 throw new RuntimeException("createWindowSurface failed " +
823                         GLUtils.getEGLErrorString(error));
824             }
825
826             if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
827                 throw new RuntimeException("eglMakeCurrent failed " +
828                         GLUtils.getEGLErrorString(mEgl.eglGetError()));
829             }
830
831             return true;
832         }
833
834
835         EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
836             int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
837             return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attrib_list);
838         }
839
840         private EGLConfig chooseEglConfig() {
841             int[] configsCount = new int[1];
842             EGLConfig[] configs = new EGLConfig[1];
843             int[] configSpec = getConfig();
844             if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
845                 throw new IllegalArgumentException("eglChooseConfig failed " +
846                         GLUtils.getEGLErrorString(mEgl.eglGetError()));
847             } else if (configsCount[0] > 0) {
848                 return configs[0];
849             }
850             return null;
851         }
852
853         private int[] getConfig() {
854             return new int[] {
855                     EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
856                     EGL_RED_SIZE, 8,
857                     EGL_GREEN_SIZE, 8,
858                     EGL_BLUE_SIZE, 8,
859                     EGL_ALPHA_SIZE, 0,
860                     EGL_DEPTH_SIZE, 0,
861                     EGL_STENCIL_SIZE, 0,
862                     EGL_CONFIG_CAVEAT, EGL_NONE,
863                     EGL_NONE
864             };
865         }
866     }
867 }