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