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