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