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