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 throw new RuntimeException("eglConfig not initialized"); 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 } 868