Jon Miranda | 16ea1b1 | 2017-12-12 14:52:48 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 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 | package com.android.wallpaper.module; |
| 17 | |
Sunny Goyal | 8600a3f | 2018-08-15 12:48:01 -0700 | [diff] [blame^] | 18 | import static javax.microedition.khronos.egl.EGL10.EGL_NO_CONTEXT; |
| 19 | import static javax.microedition.khronos.egl.EGL10.EGL_NO_SURFACE; |
| 20 | |
Jon Miranda | 16ea1b1 | 2017-12-12 14:52:48 -0800 | [diff] [blame] | 21 | import android.annotation.SuppressLint; |
| 22 | import android.app.WallpaperColors; |
| 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.content.IntentFilter; |
| 29 | import android.graphics.Bitmap; |
| 30 | import android.graphics.BitmapFactory; |
| 31 | import android.graphics.Canvas; |
| 32 | import android.graphics.Point; |
| 33 | import android.graphics.Rect; |
| 34 | import android.graphics.RectF; |
Jon Miranda | 16ea1b1 | 2017-12-12 14:52:48 -0800 | [diff] [blame] | 35 | import android.graphics.drawable.BitmapDrawable; |
| 36 | import android.graphics.drawable.Drawable; |
| 37 | import android.opengl.GLES20; |
| 38 | import android.opengl.GLUtils; |
| 39 | import android.os.AsyncTask; |
| 40 | import android.os.Build.VERSION; |
| 41 | import android.os.Build.VERSION_CODES; |
| 42 | import android.os.Handler; |
| 43 | import android.renderscript.Matrix4f; |
| 44 | import android.service.wallpaper.WallpaperService; |
Jon Miranda | 16ea1b1 | 2017-12-12 14:52:48 -0800 | [diff] [blame] | 45 | import android.util.Log; |
| 46 | import android.view.Display; |
| 47 | import android.view.MotionEvent; |
| 48 | import android.view.SurfaceHolder; |
| 49 | import android.view.WindowManager; |
| 50 | |
| 51 | import com.android.wallpaper.util.ScreenSizeCalculator; |
| 52 | |
Jon Miranda | 16ea1b1 | 2017-12-12 14:52:48 -0800 | [diff] [blame] | 53 | import java.io.FileDescriptor; |
| 54 | import java.io.FileInputStream; |
| 55 | import java.io.FileNotFoundException; |
| 56 | import java.io.IOException; |
| 57 | import java.io.PrintWriter; |
| 58 | import java.nio.ByteBuffer; |
| 59 | import java.nio.ByteOrder; |
| 60 | import java.nio.FloatBuffer; |
| 61 | |
| 62 | import javax.microedition.khronos.egl.EGL10; |
| 63 | import javax.microedition.khronos.egl.EGLConfig; |
| 64 | import javax.microedition.khronos.egl.EGLContext; |
| 65 | import javax.microedition.khronos.egl.EGLDisplay; |
| 66 | import javax.microedition.khronos.egl.EGLSurface; |
| 67 | |
Sunny Goyal | 8600a3f | 2018-08-15 12:48:01 -0700 | [diff] [blame^] | 68 | import androidx.annotation.RequiresApi; |
Jon Miranda | 16ea1b1 | 2017-12-12 14:52:48 -0800 | [diff] [blame] | 69 | |
| 70 | /** |
| 71 | * Live wallpaper service which simply renders a wallpaper from internal storage. Designed as a |
| 72 | * workaround to WallpaperManager not having an allowBackup=false option on pre-N builds of Android. |
| 73 | * <p> |
| 74 | * Adapted from {@code com.android.systemui.ImageWallpaper}. |
| 75 | */ |
| 76 | @SuppressLint("ServiceCast") |
| 77 | public class NoBackupImageWallpaper extends WallpaperService { |
| 78 | |
| 79 | public static final String ACTION_ROTATING_WALLPAPER_CHANGED = |
Jon Miranda | de0b69e | 2018-03-20 16:03:18 -0700 | [diff] [blame] | 80 | ".ACTION_ROTATING_WALLPAPER_CHANGED"; |
Jon Miranda | 16ea1b1 | 2017-12-12 14:52:48 -0800 | [diff] [blame] | 81 | public static final String PERMISSION_NOTIFY_ROTATING_WALLPAPER_CHANGED = |
Jon Miranda | de0b69e | 2018-03-20 16:03:18 -0700 | [diff] [blame] | 82 | ".NOTIFY_ROTATING_WALLPAPER_CHANGED"; |
Jon Miranda | 16ea1b1 | 2017-12-12 14:52:48 -0800 | [diff] [blame] | 83 | public static final String PREVIEW_WALLPAPER_FILE_PATH = "preview_wallpaper.jpg"; |
| 84 | public static final String ROTATING_WALLPAPER_FILE_PATH = "rotating_wallpaper.jpg"; |
| 85 | |
| 86 | private static final String TAG = "NoBackupImageWallpaper"; |
| 87 | private static final String GL_LOG_TAG = "ImageWallpaperGL"; |
| 88 | private static final boolean DEBUG = false; |
| 89 | private static final boolean FIXED_SIZED_SURFACE = false; |
| 90 | |
| 91 | private final Handler mHandler = new Handler(); |
| 92 | |
| 93 | private int mOpenGlContextCounter; |
| 94 | private WallpaperManager mWallpaperManager; |
| 95 | private DrawableEngine mEngine; |
| 96 | private boolean mIsHardwareAccelerated; |
| 97 | |
| 98 | @Override |
| 99 | public void onCreate() { |
| 100 | super.onCreate(); |
| 101 | |
| 102 | mOpenGlContextCounter = 0; |
| 103 | mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE); |
| 104 | |
| 105 | // By default, use OpenGL for drawing the static wallpaper image. |
| 106 | mIsHardwareAccelerated = true; |
| 107 | } |
| 108 | |
| 109 | @Override |
| 110 | public void onTrimMemory(int level) { |
| 111 | if (mEngine != null) { |
| 112 | mEngine.trimMemory(level); |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | @Override |
| 117 | public Engine onCreateEngine() { |
| 118 | mEngine = new DrawableEngine(); |
| 119 | return mEngine; |
| 120 | } |
| 121 | |
| 122 | private class DrawableEngine extends Engine { |
| 123 | static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; |
| 124 | static final int EGL_OPENGL_ES2_BIT = 4; |
| 125 | private static final String S_SIMPLE_VS = |
| 126 | "attribute vec4 position;\n" |
| 127 | + "attribute vec2 texCoords;\n" |
| 128 | + "varying vec2 outTexCoords;\n" |
| 129 | + "uniform mat4 projection;\n" |
| 130 | + "\nvoid main(void) {\n" |
| 131 | + " outTexCoords = texCoords;\n" |
| 132 | + " gl_Position = projection * position;\n" |
| 133 | + "}\n\n"; |
| 134 | private static final String S_SIMPLE_FS = |
| 135 | "precision mediump float;\n\n" |
| 136 | + "varying vec2 outTexCoords;\n" |
| 137 | + "uniform sampler2D texture;\n" |
| 138 | + "\nvoid main(void) {\n" |
| 139 | + " gl_FragColor = texture2D(texture, outTexCoords);\n" |
| 140 | + "}\n\n"; |
| 141 | private static final int FLOAT_SIZE_BYTES = 4; |
| 142 | private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES; |
| 143 | private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0; |
| 144 | private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3; |
| 145 | Bitmap mBackground; |
| 146 | WallpaperColors mCachedWallpaperColors; |
| 147 | int mBackgroundWidth = -1, mBackgroundHeight = -1; |
| 148 | int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1; |
| 149 | int mLastRotation = -1; |
| 150 | float mXOffset = 0.5f; |
| 151 | float mYOffset = 0.5f; |
| 152 | float mScale = 1f; |
| 153 | boolean mVisible = true; |
| 154 | boolean mOffsetsChanged; |
| 155 | int mLastXTranslation; |
| 156 | int mLastYTranslation; |
| 157 | private Display mDefaultDisplay; |
| 158 | private EGL10 mEgl; |
| 159 | private EGLDisplay mEglDisplay; |
| 160 | private EGLConfig mEglConfig; |
| 161 | private EGLContext mEglContext; |
| 162 | private EGLSurface mEglSurface; |
| 163 | private int mTexture; |
| 164 | private int mProgram; |
| 165 | private boolean mIsOpenGlTextureLoaded; |
| 166 | private int mRotationAtLastSurfaceSizeUpdate = -1; |
| 167 | private int mDisplayWidthAtLastSurfaceSizeUpdate = -1; |
| 168 | private int mDisplayHeightAtLastSurfaceSizeUpdate = -1; |
| 169 | |
| 170 | private int mLastRequestedWidth = -1; |
| 171 | private int mLastRequestedHeight = -1; |
| 172 | private AsyncTask<Void, Void, Bitmap> mLoader; |
| 173 | private boolean mNeedsDrawAfterLoadingWallpaper; |
| 174 | private boolean mSurfaceValid; |
| 175 | |
| 176 | private BroadcastReceiver mReceiver; |
| 177 | |
| 178 | public DrawableEngine() { |
| 179 | super(); |
| 180 | } |
| 181 | |
| 182 | public void trimMemory(int level) { |
| 183 | if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW |
| 184 | && mBackground != null) { |
| 185 | if (DEBUG) { |
| 186 | Log.d(TAG, "trimMemory"); |
| 187 | } |
| 188 | mBackground.recycle(); |
| 189 | mBackground = null; |
| 190 | mBackgroundWidth = -1; |
| 191 | mBackgroundHeight = -1; |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | @Override |
| 196 | public void onCreate(SurfaceHolder surfaceHolder) { |
| 197 | if (DEBUG) { |
| 198 | Log.d(TAG, "onCreate"); |
| 199 | } |
| 200 | |
| 201 | super.onCreate(surfaceHolder); |
| 202 | |
| 203 | mIsOpenGlTextureLoaded = false; |
| 204 | |
| 205 | mDefaultDisplay = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)) |
| 206 | .getDefaultDisplay(); |
| 207 | |
| 208 | updateSurfaceSize(surfaceHolder, mDefaultDisplay, false /* forDraw */); |
| 209 | |
| 210 | // Enable offset notifications to pan wallpaper for parallax effect. |
| 211 | setOffsetNotificationsEnabled(true); |
| 212 | |
| 213 | // If not a preview, then register a local broadcast receiver for listening to changes in the |
| 214 | // rotating wallpaper file. |
| 215 | if (!isPreview()) { |
| 216 | IntentFilter filter = new IntentFilter(); |
Jon Miranda | de0b69e | 2018-03-20 16:03:18 -0700 | [diff] [blame] | 217 | filter.addAction(getPackageName() + ACTION_ROTATING_WALLPAPER_CHANGED); |
Jon Miranda | 16ea1b1 | 2017-12-12 14:52:48 -0800 | [diff] [blame] | 218 | |
| 219 | mReceiver = new BroadcastReceiver() { |
| 220 | @Override |
| 221 | public void onReceive(Context context, Intent intent) { |
| 222 | if (DEBUG) { |
| 223 | Log.i(TAG, "Broadcast received with intent: " + intent); |
| 224 | } |
| 225 | |
| 226 | String action = intent.getAction(); |
Jon Miranda | de0b69e | 2018-03-20 16:03:18 -0700 | [diff] [blame] | 227 | if (action.equals(getPackageName() + ACTION_ROTATING_WALLPAPER_CHANGED)) { |
Jon Miranda | 16ea1b1 | 2017-12-12 14:52:48 -0800 | [diff] [blame] | 228 | DrawableEngine.this.invalidateAndRedrawWallpaper(); |
| 229 | } |
| 230 | } |
| 231 | }; |
| 232 | |
Jon Miranda | de0b69e | 2018-03-20 16:03:18 -0700 | [diff] [blame] | 233 | registerReceiver(mReceiver, filter, getPackageName() |
| 234 | + PERMISSION_NOTIFY_ROTATING_WALLPAPER_CHANGED, null /* handler */); |
Jon Miranda | 16ea1b1 | 2017-12-12 14:52:48 -0800 | [diff] [blame] | 235 | } |
| 236 | } |
| 237 | |
| 238 | @Override |
| 239 | public void onDestroy() { |
| 240 | super.onDestroy(); |
| 241 | mBackground = null; |
| 242 | mWallpaperManager.forgetLoadedWallpaper(); |
| 243 | |
| 244 | if (!isPreview() && mReceiver != null) { |
| 245 | unregisterReceiver(mReceiver); |
| 246 | } |
| 247 | } |
| 248 | |
| 249 | boolean updateSurfaceSize(SurfaceHolder surfaceHolder, Display display, boolean forDraw) { |
| 250 | boolean hasWallpaper = true; |
| 251 | Point displaySize = ScreenSizeCalculator.getInstance().getScreenSize(display); |
| 252 | |
| 253 | // Load background image dimensions, if we haven't saved them yet |
| 254 | if (mBackgroundWidth <= 0 || mBackgroundHeight <= 0) { |
| 255 | // Need to load the image to get dimensions |
| 256 | loadWallpaper(forDraw); |
| 257 | if (DEBUG) { |
| 258 | Log.d(TAG, "Reloading, redoing updateSurfaceSize later."); |
| 259 | } |
| 260 | hasWallpaper = false; |
| 261 | } |
| 262 | |
| 263 | // Force the wallpaper to cover the screen in both dimensions |
| 264 | int surfaceWidth = Math.max(displaySize.x, mBackgroundWidth); |
| 265 | int surfaceHeight = Math.max(displaySize.y, mBackgroundHeight); |
| 266 | |
| 267 | if (FIXED_SIZED_SURFACE) { |
| 268 | // Used a fixed size surface, because we are special. We can do |
| 269 | // this because we know the current design of window animations doesn't |
| 270 | // cause this to break. |
| 271 | surfaceHolder.setFixedSize(surfaceWidth, surfaceHeight); |
| 272 | mLastRequestedWidth = surfaceWidth; |
| 273 | mLastRequestedHeight = surfaceHeight; |
| 274 | } else { |
| 275 | surfaceHolder.setSizeFromLayout(); |
| 276 | } |
| 277 | return hasWallpaper; |
| 278 | } |
| 279 | |
| 280 | @Override |
| 281 | public void onVisibilityChanged(boolean visible) { |
| 282 | if (DEBUG) { |
| 283 | Log.d(TAG, "onVisibilityChanged: mVisible, visible=" + mVisible + ", " + visible); |
| 284 | } |
| 285 | |
| 286 | if (mVisible != visible) { |
| 287 | if (DEBUG) { |
| 288 | Log.d(TAG, "Visibility changed to visible=" + visible); |
| 289 | } |
| 290 | mVisible = visible; |
| 291 | drawFrame(false /* forceRedraw */); |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | @Override |
| 296 | public void onTouchEvent(MotionEvent event) { |
| 297 | super.onTouchEvent(event); |
| 298 | } |
| 299 | |
| 300 | @Override |
| 301 | public void onOffsetsChanged(float xOffset, float yOffset, |
| 302 | float xOffsetStep, float yOffsetStep, |
| 303 | int xPixels, int yPixels) { |
| 304 | if (DEBUG) { |
| 305 | Log.d(TAG, "onOffsetsChanged: xOffset=" + xOffset + ", yOffset=" + yOffset |
| 306 | + ", xOffsetStep=" + xOffsetStep + ", yOffsetStep=" + yOffsetStep |
| 307 | + ", xPixels=" + xPixels + ", yPixels=" + yPixels); |
| 308 | } |
| 309 | |
| 310 | if (mXOffset != xOffset || mYOffset != yOffset) { |
| 311 | if (DEBUG) { |
| 312 | Log.d(TAG, "Offsets changed to (" + xOffset + "," + yOffset + ")."); |
| 313 | } |
| 314 | mXOffset = xOffset; |
| 315 | mYOffset = yOffset; |
| 316 | mOffsetsChanged = true; |
| 317 | } |
| 318 | mHandler.post(new Runnable() { |
| 319 | @Override |
| 320 | public void run() { |
| 321 | drawFrame(false /* forceRedraw */); |
| 322 | } |
| 323 | }); |
| 324 | } |
| 325 | |
| 326 | @Override |
| 327 | public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { |
| 328 | if (DEBUG) { |
| 329 | Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height); |
| 330 | } |
| 331 | |
| 332 | super.onSurfaceChanged(holder, format, width, height); |
| 333 | |
| 334 | // Retrieve buffer in new size. |
| 335 | if (mEgl != null) { |
| 336 | mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); |
| 337 | } |
| 338 | drawFrame(false /* forceRedraw */); |
| 339 | } |
| 340 | |
| 341 | @Override |
| 342 | public void onSurfaceDestroyed(SurfaceHolder holder) { |
| 343 | super.onSurfaceDestroyed(holder); |
| 344 | if (DEBUG) { |
| 345 | Log.d(TAG, "onSurfaceDestroyed"); |
| 346 | } |
| 347 | mLastSurfaceWidth = mLastSurfaceHeight = -1; |
| 348 | mSurfaceValid = false; |
| 349 | |
| 350 | if (mIsHardwareAccelerated) { |
| 351 | finishGL(mTexture, mProgram); |
| 352 | } |
| 353 | } |
| 354 | |
| 355 | @Override |
| 356 | public void onSurfaceCreated(SurfaceHolder holder) { |
| 357 | super.onSurfaceCreated(holder); |
| 358 | if (DEBUG) { |
| 359 | Log.d(TAG, "onSurfaceCreated"); |
| 360 | } |
| 361 | mLastSurfaceWidth = mLastSurfaceHeight = -1; |
| 362 | mSurfaceValid = true; |
| 363 | |
| 364 | if (mIsHardwareAccelerated) { |
| 365 | if (!initGL(holder)) { |
| 366 | // Fall back to canvas drawing if initializing OpenGL failed. |
| 367 | mIsHardwareAccelerated = false; |
| 368 | mEgl = null; |
| 369 | } |
| 370 | } |
| 371 | } |
| 372 | |
| 373 | @Override |
| 374 | public void onSurfaceRedrawNeeded(SurfaceHolder holder) { |
| 375 | if (DEBUG) { |
| 376 | Log.d(TAG, "onSurfaceRedrawNeeded"); |
| 377 | } |
| 378 | super.onSurfaceRedrawNeeded(holder); |
| 379 | |
| 380 | drawFrame(true /* forceRedraw */); |
| 381 | } |
| 382 | |
| 383 | @RequiresApi(VERSION_CODES.O_MR1) |
| 384 | @Override |
| 385 | public WallpaperColors onComputeColors() { |
| 386 | // It's OK to return null here. |
| 387 | return mCachedWallpaperColors; |
| 388 | } |
| 389 | |
| 390 | /** |
| 391 | * Invalidates the currently-drawn wallpaper image, causing the engine to reload the image from |
| 392 | * disk and draw the new wallpaper image. |
| 393 | */ |
| 394 | public void invalidateAndRedrawWallpaper() { |
| 395 | // If a wallpaper load was already in flight, cancel it and restart a load in order to decode |
| 396 | // the new image. |
| 397 | if (mLoader != null) { |
| 398 | mLoader.cancel(true /* mayInterruptIfRunning */); |
| 399 | mLoader = null; |
| 400 | } |
| 401 | |
| 402 | loadWallpaper(true /* needsDraw */); |
| 403 | } |
| 404 | |
| 405 | void drawFrame(boolean forceRedraw) { |
| 406 | if (!mSurfaceValid) { |
| 407 | return; |
| 408 | } |
| 409 | |
| 410 | Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(mDefaultDisplay); |
| 411 | int newRotation = mDefaultDisplay.getRotation(); |
| 412 | |
| 413 | // Sometimes a wallpaper is not large enough to cover the screen in one dimension. |
| 414 | // Call updateSurfaceSize -- it will only actually do the update if the dimensions |
| 415 | // should change |
| 416 | if (newRotation != mLastRotation) { |
| 417 | // Update surface size (if necessary) |
| 418 | if (!updateSurfaceSize(getSurfaceHolder(), mDefaultDisplay, true /* forDraw */)) { |
| 419 | return; |
| 420 | } |
| 421 | mRotationAtLastSurfaceSizeUpdate = newRotation; |
| 422 | mDisplayWidthAtLastSurfaceSizeUpdate = screenSize.x; |
| 423 | mDisplayHeightAtLastSurfaceSizeUpdate = screenSize.y; |
| 424 | } |
| 425 | SurfaceHolder sh = getSurfaceHolder(); |
| 426 | final Rect frame = sh.getSurfaceFrame(); |
| 427 | final int dw = frame.width(); |
| 428 | final int dh = frame.height(); |
| 429 | boolean surfaceDimensionsChanged = dw != mLastSurfaceWidth |
| 430 | || dh != mLastSurfaceHeight; |
| 431 | |
| 432 | boolean redrawNeeded = surfaceDimensionsChanged || newRotation != mLastRotation |
| 433 | || forceRedraw; |
| 434 | if (!redrawNeeded && !mOffsetsChanged) { |
| 435 | if (DEBUG) { |
| 436 | Log.d(TAG, "Suppressed drawFrame since redraw is not needed " |
| 437 | + "and offsets have not changed."); |
| 438 | } |
| 439 | return; |
| 440 | } |
| 441 | mLastRotation = newRotation; |
| 442 | |
| 443 | // Load bitmap if its null and we're not using hardware acceleration. |
| 444 | if ((mIsHardwareAccelerated && !mIsOpenGlTextureLoaded) // Using OpenGL but texture not loaded |
| 445 | || (!mIsHardwareAccelerated && mBackground == null)) { // Draw with Canvas but no bitmap |
| 446 | if (DEBUG) { |
| 447 | Log.d(TAG, "Reloading bitmap: mBackground, bgw, bgh, dw, dh = " |
| 448 | + mBackground + ", " + ((mBackground == null) ? 0 : mBackground.getWidth()) + ", " |
| 449 | + ((mBackground == null) ? 0 : mBackground.getHeight()) + ", " + dw + ", " + dh); |
| 450 | } |
| 451 | loadWallpaper(true /* needDraw */); |
| 452 | if (DEBUG) { |
| 453 | Log.d(TAG, "Reloading, resuming draw later"); |
| 454 | } |
| 455 | return; |
| 456 | } |
| 457 | |
| 458 | // Center the scaled image |
| 459 | mScale = Math.max(1f, Math.max(dw / (float) mBackgroundWidth, |
| 460 | dh / (float) mBackgroundHeight)); |
| 461 | final int availw = dw - (int) (mBackgroundWidth * mScale); |
| 462 | final int availh = dh - (int) (mBackgroundHeight * mScale); |
| 463 | int xPixels = availw / 2; |
| 464 | int yPixels = availh / 2; |
| 465 | |
| 466 | // Adjust the image for xOffset/yOffset values. If window manager is handling offsets, |
| 467 | // mXOffset and mYOffset are set to 0.5f by default and therefore xPixels and yPixels |
| 468 | // will remain unchanged |
| 469 | final int availwUnscaled = dw - mBackgroundWidth; |
| 470 | final int availhUnscaled = dh - mBackgroundHeight; |
| 471 | if (availwUnscaled < 0) { |
| 472 | xPixels += (int) (availwUnscaled * (mXOffset - .5f) + .5f); |
| 473 | } |
| 474 | if (availhUnscaled < 0) { |
| 475 | yPixels += (int) (availhUnscaled * (mYOffset - .5f) + .5f); |
| 476 | } |
| 477 | |
| 478 | mOffsetsChanged = false; |
| 479 | if (surfaceDimensionsChanged) { |
| 480 | mLastSurfaceWidth = dw; |
| 481 | mLastSurfaceHeight = dh; |
| 482 | } |
| 483 | if (!redrawNeeded && xPixels == mLastXTranslation && yPixels == mLastYTranslation) { |
| 484 | if (DEBUG) { |
| 485 | Log.d(TAG, "Suppressed drawFrame since the image has not " |
| 486 | + "actually moved an integral number of pixels."); |
| 487 | } |
| 488 | return; |
| 489 | } |
| 490 | mLastXTranslation = xPixels; |
| 491 | mLastYTranslation = yPixels; |
| 492 | |
| 493 | if (DEBUG) { |
| 494 | Log.d(TAG, "Redrawing wallpaper"); |
| 495 | } |
| 496 | |
| 497 | if (mIsHardwareAccelerated) { |
| 498 | if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) { |
| 499 | drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels); |
| 500 | } else { |
| 501 | // If OpenGL drawing was successful, then we can safely discard a reference to the |
| 502 | // wallpaper bitmap to save memory (since a copy has already been loaded into an OpenGL |
| 503 | // texture). |
| 504 | mBackground = null; |
| 505 | } |
| 506 | } else { |
| 507 | drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels); |
| 508 | } |
| 509 | } |
| 510 | |
| 511 | /** |
| 512 | * Loads the wallpaper on background thread and schedules updating the surface frame, |
| 513 | * and if {@param needsDraw} is set also draws a frame. |
| 514 | * <p> |
| 515 | * If loading is already in-flight, subsequent loads are ignored (but needDraw is or-ed to |
| 516 | * the active request). |
| 517 | * <p> |
| 518 | * If {@param needsReset} is set also clears the cache in WallpaperManager first. |
| 519 | */ |
| 520 | private void loadWallpaper(boolean needsDraw) { |
| 521 | mNeedsDrawAfterLoadingWallpaper |= needsDraw; |
| 522 | if (mLoader != null) { |
| 523 | if (DEBUG) { |
| 524 | Log.d(TAG, "Skipping loadWallpaper, already in flight "); |
| 525 | } |
| 526 | return; |
| 527 | } |
| 528 | mLoader = new AsyncTask<Void, Void, Bitmap>() { |
| 529 | @Override |
| 530 | protected Bitmap doInBackground(Void... params) { |
| 531 | Throwable exception = null; |
| 532 | try { |
| 533 | // Decode bitmap of rotating image wallpaper. |
| 534 | String wallpaperFilePath = isPreview() |
| 535 | ? PREVIEW_WALLPAPER_FILE_PATH : ROTATING_WALLPAPER_FILE_PATH; |
Santiago Etchebehere | 5a7f1dd | 2018-04-03 15:01:29 -0700 | [diff] [blame] | 536 | Context context = isPreview() ? getApplicationContext() |
| 537 | : getApplicationContext().createDeviceProtectedStorageContext(); |
| 538 | FileInputStream fileInputStream = context.openFileInput(wallpaperFilePath); |
Jon Miranda | 16ea1b1 | 2017-12-12 14:52:48 -0800 | [diff] [blame] | 539 | Bitmap bitmap = BitmapFactory.decodeStream(fileInputStream); |
| 540 | fileInputStream.close(); |
| 541 | return bitmap; |
| 542 | } catch (RuntimeException | FileNotFoundException | OutOfMemoryError e) { |
| 543 | Log.i(TAG, "couldn't decode stream: ", e); |
| 544 | exception = e; |
| 545 | } catch (IOException e) { |
| 546 | Log.i(TAG, "couldn't close stream: ", e); |
| 547 | exception = e; |
| 548 | } |
| 549 | |
| 550 | if (isCancelled()) { |
| 551 | return null; |
| 552 | } |
| 553 | |
| 554 | if (exception != null) { |
| 555 | // Note that if we do fail at this, and the default wallpaper can't |
| 556 | // be loaded, we will go into a cycle. Don't do a build where the |
| 557 | // default wallpaper can't be loaded. |
| 558 | Log.w(TAG, "Unable to load wallpaper!", exception); |
| 559 | try { |
| 560 | return ((BitmapDrawable) getFallbackDrawable()).getBitmap(); |
| 561 | } catch (OutOfMemoryError ex) { |
| 562 | // now we're really screwed. |
| 563 | Log.w(TAG, "Unable reset to default wallpaper!", ex); |
| 564 | } |
| 565 | |
| 566 | if (isCancelled()) { |
| 567 | return null; |
| 568 | } |
| 569 | } |
| 570 | return null; |
| 571 | } |
| 572 | |
| 573 | @Override |
| 574 | protected void onPostExecute(Bitmap b) { |
| 575 | mBackground = null; |
| 576 | mBackgroundWidth = -1; |
| 577 | mBackgroundHeight = -1; |
| 578 | |
| 579 | if (b != null) { |
| 580 | mBackground = b; |
| 581 | mBackgroundWidth = mBackground.getWidth(); |
| 582 | mBackgroundHeight = mBackground.getHeight(); |
| 583 | |
| 584 | if (VERSION.SDK_INT >= VERSION_CODES.O_MR1) { |
| 585 | mCachedWallpaperColors = WallpaperColors.fromBitmap(mBackground); |
| 586 | notifyColorsChanged(); |
| 587 | } |
| 588 | } |
| 589 | |
| 590 | if (DEBUG) { |
| 591 | Log.d(TAG, "Wallpaper loaded: " + mBackground); |
| 592 | } |
| 593 | updateSurfaceSize(getSurfaceHolder(), mDefaultDisplay, |
| 594 | false /* forDraw */); |
| 595 | if (mTexture != 0 && mEgl != null) { |
| 596 | deleteTexture(mTexture); |
| 597 | } |
| 598 | // If background is absent (due to an error decoding the bitmap) then don't try to load |
| 599 | // a texture. |
| 600 | if (mEgl != null && mBackground != null) { |
| 601 | mTexture = loadTexture(mBackground); |
| 602 | } |
| 603 | if (mNeedsDrawAfterLoadingWallpaper) { |
| 604 | drawFrame(true /* forceRedraw */); |
| 605 | } |
| 606 | |
| 607 | mLoader = null; |
| 608 | mNeedsDrawAfterLoadingWallpaper = false; |
| 609 | } |
| 610 | }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
| 611 | } |
| 612 | |
| 613 | private Drawable getFallbackDrawable() { |
| 614 | Drawable drawable; |
| 615 | try { |
| 616 | drawable = mWallpaperManager.getDrawable(); |
| 617 | } catch (java.lang.Exception e) { |
| 618 | // Work around Samsung bug where SecurityException is thrown if device is still using its |
| 619 | // default wallpaper, and around Android 7.0 bug where SELinux issues can cause a perfectly |
| 620 | // valid access of the current wallpaper to cause a failed Binder transaction manifest here |
| 621 | // as a RuntimeException. |
| 622 | drawable = mWallpaperManager.getBuiltInDrawable(); |
| 623 | } |
| 624 | return drawable; |
| 625 | } |
| 626 | |
| 627 | @Override |
| 628 | protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) { |
| 629 | super.dump(prefix, fd, out, args); |
| 630 | |
| 631 | out.print(prefix); |
| 632 | out.println("ImageWallpaper.DrawableEngine:"); |
| 633 | out.print(prefix); |
| 634 | out.print(" mBackground="); |
| 635 | out.print(mBackground); |
| 636 | out.print(" mBackgroundWidth="); |
| 637 | out.print(mBackgroundWidth); |
| 638 | out.print(" mBackgroundHeight="); |
| 639 | out.println(mBackgroundHeight); |
| 640 | |
| 641 | out.print(prefix); |
| 642 | out.print(" mLastRotation="); |
| 643 | out.print(mLastRotation); |
| 644 | out.print(" mLastSurfaceWidth="); |
| 645 | out.print(mLastSurfaceWidth); |
| 646 | out.print(" mLastSurfaceHeight="); |
| 647 | out.println(mLastSurfaceHeight); |
| 648 | |
| 649 | out.print(prefix); |
| 650 | out.print(" mXOffset="); |
| 651 | out.print(mXOffset); |
| 652 | out.print(" mYOffset="); |
| 653 | out.println(mYOffset); |
| 654 | |
| 655 | out.print(prefix); |
| 656 | out.print(" mVisible="); |
| 657 | out.print(mVisible); |
| 658 | out.print(" mOffsetsChanged="); |
| 659 | out.println(mOffsetsChanged); |
| 660 | |
| 661 | out.print(prefix); |
| 662 | out.print(" mLastXTranslation="); |
| 663 | out.print(mLastXTranslation); |
| 664 | out.print(" mLastYTranslation="); |
| 665 | out.print(mLastYTranslation); |
| 666 | out.print(" mScale="); |
| 667 | out.println(mScale); |
| 668 | |
| 669 | out.print(prefix); |
| 670 | out.print(" mLastRequestedWidth="); |
| 671 | out.print(mLastRequestedWidth); |
| 672 | out.print(" mLastRequestedHeight="); |
| 673 | out.println(mLastRequestedHeight); |
| 674 | |
| 675 | out.print(prefix); |
| 676 | out.println(" DisplayInfo at last updateSurfaceSize:"); |
| 677 | out.print(prefix); |
| 678 | out.print(" rotation="); |
| 679 | out.print(mRotationAtLastSurfaceSizeUpdate); |
| 680 | out.print(" width="); |
| 681 | out.print(mDisplayWidthAtLastSurfaceSizeUpdate); |
| 682 | out.print(" height="); |
| 683 | out.println(mDisplayHeightAtLastSurfaceSizeUpdate); |
| 684 | } |
| 685 | |
| 686 | private void drawWallpaperWithCanvas(SurfaceHolder sh, int w, int h, int left, int top) { |
| 687 | Canvas c = sh.lockCanvas(); |
| 688 | if (c != null) { |
| 689 | try { |
| 690 | if (DEBUG) { |
| 691 | Log.d(TAG, "Redrawing: left=" + left + ", top=" + top); |
| 692 | } |
| 693 | |
| 694 | final float right = left + mBackgroundWidth * mScale; |
| 695 | final float bottom = top + mBackgroundHeight * mScale; |
| 696 | if (w < 0 || h < 0) { |
Santiago Etchebehere | c9e6ec0 | 2018-03-30 10:19:58 -0700 | [diff] [blame] | 697 | c.save(); |
| 698 | c.clipOutRect(left, top, right, bottom); |
Jon Miranda | 16ea1b1 | 2017-12-12 14:52:48 -0800 | [diff] [blame] | 699 | c.drawColor(0xff000000); |
| 700 | c.restore(); |
| 701 | } |
| 702 | if (mBackground != null) { |
| 703 | RectF dest = new RectF(left, top, right, bottom); |
| 704 | // add a filter bitmap? |
| 705 | c.drawBitmap(mBackground, null, dest, null); |
| 706 | } |
| 707 | } finally { |
| 708 | sh.unlockCanvasAndPost(c); |
| 709 | } |
| 710 | } |
| 711 | } |
| 712 | |
| 713 | private boolean drawWallpaperWithOpenGL(SurfaceHolder sh, int w, int h, int left, int top) { |
| 714 | |
| 715 | mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); |
| 716 | |
| 717 | final float right = left + mBackgroundWidth * mScale; |
| 718 | final float bottom = top + mBackgroundHeight * mScale; |
| 719 | |
| 720 | final Rect frame = sh.getSurfaceFrame(); |
| 721 | final Matrix4f ortho = new Matrix4f(); |
| 722 | ortho.loadOrtho(0.0f, frame.width(), frame.height(), 0.0f, -1.0f, 1.0f); |
| 723 | |
| 724 | final FloatBuffer triangleVertices = createMesh(left, top, right, bottom); |
| 725 | |
| 726 | final int attribPosition = GLES20.glGetAttribLocation(mProgram, "position"); |
| 727 | final int attribTexCoords = GLES20.glGetAttribLocation(mProgram, "texCoords"); |
| 728 | final int uniformTexture = GLES20.glGetUniformLocation(mProgram, "texture"); |
| 729 | final int uniformProjection = GLES20.glGetUniformLocation(mProgram, "projection"); |
| 730 | |
| 731 | checkGlError(); |
| 732 | |
| 733 | GLES20.glViewport(0, 0, frame.width(), frame.height()); |
| 734 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexture); |
| 735 | |
| 736 | GLES20.glUseProgram(mProgram); |
| 737 | GLES20.glEnableVertexAttribArray(attribPosition); |
| 738 | GLES20.glEnableVertexAttribArray(attribTexCoords); |
| 739 | GLES20.glUniform1i(uniformTexture, 0); |
| 740 | GLES20.glUniformMatrix4fv(uniformProjection, 1, false, ortho.getArray(), 0); |
| 741 | |
| 742 | checkGlError(); |
| 743 | |
| 744 | if (w > 0 || h > 0) { |
| 745 | GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); |
| 746 | GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); |
| 747 | } |
| 748 | |
| 749 | // drawQuad |
| 750 | triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET); |
| 751 | GLES20.glVertexAttribPointer(attribPosition, 3, GLES20.GL_FLOAT, false, |
| 752 | TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices); |
| 753 | |
| 754 | triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET); |
| 755 | GLES20.glVertexAttribPointer(attribTexCoords, 3, GLES20.GL_FLOAT, false, |
| 756 | TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices); |
| 757 | |
| 758 | GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); |
| 759 | |
| 760 | boolean status = mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); |
| 761 | checkEglError(); |
| 762 | |
| 763 | return status; |
| 764 | } |
| 765 | |
| 766 | private FloatBuffer createMesh(int left, int top, float right, float bottom) { |
| 767 | final float[] verticesData = { |
| 768 | // X, Y, Z, U, V |
| 769 | left, bottom, 0.0f, 0.0f, 1.0f, |
| 770 | right, bottom, 0.0f, 1.0f, 1.0f, |
| 771 | left, top, 0.0f, 0.0f, 0.0f, |
| 772 | right, top, 0.0f, 1.0f, 0.0f, |
| 773 | }; |
| 774 | |
| 775 | final int bytes = verticesData.length * FLOAT_SIZE_BYTES; |
| 776 | final FloatBuffer triangleVertices = ByteBuffer.allocateDirect(bytes).order( |
| 777 | ByteOrder.nativeOrder()).asFloatBuffer(); |
| 778 | triangleVertices.put(verticesData).position(0); |
| 779 | return triangleVertices; |
| 780 | } |
| 781 | |
| 782 | private int loadTexture(Bitmap bitmap) { |
| 783 | mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); |
| 784 | |
| 785 | int[] textures = new int[1]; |
| 786 | |
| 787 | GLES20.glActiveTexture(GLES20.GL_TEXTURE0); |
| 788 | GLES20.glGenTextures(1, textures, 0); |
| 789 | checkGlError(); |
| 790 | |
| 791 | int texture = textures[0]; |
| 792 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture); |
| 793 | checkGlError(); |
| 794 | |
| 795 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); |
| 796 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); |
| 797 | |
| 798 | GLES20.glTexParameteri( |
| 799 | GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); |
| 800 | GLES20.glTexParameteri( |
| 801 | GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); |
| 802 | |
| 803 | GLUtils.texImage2D( |
| 804 | GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, bitmap, GLES20.GL_UNSIGNED_BYTE, 0); |
| 805 | checkGlError(); |
| 806 | |
| 807 | mIsOpenGlTextureLoaded = true; |
| 808 | |
| 809 | return texture; |
| 810 | } |
| 811 | |
| 812 | private int buildProgram(String vertex, String fragment) { |
| 813 | int vertexShader = buildShader(vertex, GLES20.GL_VERTEX_SHADER); |
| 814 | if (vertexShader == 0) { |
| 815 | return 0; |
| 816 | } |
| 817 | |
| 818 | int fragmentShader = buildShader(fragment, GLES20.GL_FRAGMENT_SHADER); |
| 819 | if (fragmentShader == 0) { |
| 820 | return 0; |
| 821 | } |
| 822 | |
| 823 | int program = GLES20.glCreateProgram(); |
| 824 | GLES20.glAttachShader(program, vertexShader); |
| 825 | GLES20.glAttachShader(program, fragmentShader); |
| 826 | GLES20.glLinkProgram(program); |
| 827 | checkGlError(); |
| 828 | |
| 829 | GLES20.glDeleteShader(vertexShader); |
| 830 | GLES20.glDeleteShader(fragmentShader); |
| 831 | |
| 832 | int[] status = new int[1]; |
| 833 | GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, status, 0); |
| 834 | if (status[0] != GLES20.GL_TRUE) { |
| 835 | String error = GLES20.glGetProgramInfoLog(program); |
| 836 | Log.d(GL_LOG_TAG, "Error while linking program:\n" + error); |
| 837 | GLES20.glDeleteProgram(program); |
| 838 | return 0; |
| 839 | } |
| 840 | |
| 841 | return program; |
| 842 | } |
| 843 | |
| 844 | private int buildShader(String source, int type) { |
| 845 | int shader = GLES20.glCreateShader(type); |
| 846 | |
| 847 | GLES20.glShaderSource(shader, source); |
| 848 | checkGlError(); |
| 849 | |
| 850 | GLES20.glCompileShader(shader); |
| 851 | checkGlError(); |
| 852 | |
| 853 | int[] status = new int[1]; |
| 854 | GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, status, 0); |
| 855 | if (status[0] != GLES20.GL_TRUE) { |
| 856 | String error = GLES20.glGetShaderInfoLog(shader); |
| 857 | Log.d(GL_LOG_TAG, "Error while compiling shader:\n" + error); |
| 858 | GLES20.glDeleteShader(shader); |
| 859 | return 0; |
| 860 | } |
| 861 | |
| 862 | return shader; |
| 863 | } |
| 864 | |
| 865 | private void checkEglError() { |
| 866 | int error = mEgl.eglGetError(); |
| 867 | if (error != EGL10.EGL_SUCCESS) { |
| 868 | Log.w(GL_LOG_TAG, "EGL error = " + GLUtils.getEGLErrorString(error)); |
| 869 | } |
| 870 | } |
| 871 | |
| 872 | private void checkGlError() { |
| 873 | int error = GLES20.glGetError(); |
| 874 | if (error != GLES20.GL_NO_ERROR) { |
| 875 | Log.w(GL_LOG_TAG, "GL error = 0x" + Integer.toHexString(error), new Throwable()); |
| 876 | } |
| 877 | } |
| 878 | |
| 879 | private void deleteTexture(int texture) { |
| 880 | int[] textures = new int[1]; |
| 881 | textures[0] = texture; |
| 882 | |
| 883 | mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); |
| 884 | GLES20.glDeleteTextures(1, textures, 0); |
| 885 | mTexture = 0; |
| 886 | } |
| 887 | |
| 888 | private void finishGL(int texture, int program) { |
| 889 | if (mEgl == null) { |
| 890 | return; |
| 891 | } |
| 892 | |
| 893 | mOpenGlContextCounter--; |
| 894 | |
| 895 | mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); |
| 896 | deleteTexture(mTexture); |
| 897 | GLES20.glDeleteProgram(program); |
| 898 | mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
| 899 | mEgl.eglDestroySurface(mEglDisplay, mEglSurface); |
| 900 | mEgl.eglDestroyContext(mEglDisplay, mEglContext); |
| 901 | if (mOpenGlContextCounter == 0) { |
| 902 | mEgl.eglTerminate(mEglDisplay); |
| 903 | } |
| 904 | |
| 905 | mEgl = null; |
| 906 | } |
| 907 | |
| 908 | private boolean initGL(SurfaceHolder surfaceHolder) { |
| 909 | mEgl = (EGL10) EGLContext.getEGL(); |
| 910 | |
| 911 | mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); |
| 912 | if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { |
| 913 | throw new RuntimeException("eglGetDisplay failed " |
| 914 | + GLUtils.getEGLErrorString(mEgl.eglGetError())); |
| 915 | } |
| 916 | |
| 917 | int[] version = new int[2]; |
| 918 | if (!mEgl.eglInitialize(mEglDisplay, version)) { |
| 919 | throw new RuntimeException("eglInitialize failed " |
| 920 | + GLUtils.getEGLErrorString(mEgl.eglGetError())); |
| 921 | } |
| 922 | |
| 923 | mOpenGlContextCounter++; |
| 924 | |
| 925 | mEglConfig = chooseEglConfig(); |
| 926 | if (mEglConfig == null) { |
| 927 | throw new RuntimeException("eglConfig not initialized"); |
| 928 | } |
| 929 | |
| 930 | mEglContext = createContext(mEgl, mEglDisplay, mEglConfig); |
| 931 | if (mEglContext == EGL_NO_CONTEXT) { |
| 932 | throw new RuntimeException("createContext failed " |
| 933 | + GLUtils.getEGLErrorString(mEgl.eglGetError())); |
| 934 | } |
| 935 | |
| 936 | int attribs[] = { |
| 937 | EGL10.EGL_WIDTH, 1, |
| 938 | EGL10.EGL_HEIGHT, 1, |
| 939 | EGL10.EGL_NONE |
| 940 | }; |
| 941 | EGLSurface tmpSurface = mEgl.eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs); |
| 942 | mEgl.eglMakeCurrent(mEglDisplay, tmpSurface, tmpSurface, mEglContext); |
| 943 | |
| 944 | int[] maxSize = new int[1]; |
| 945 | Rect frame = surfaceHolder.getSurfaceFrame(); |
| 946 | GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxSize, 0); |
| 947 | |
| 948 | mEgl.eglMakeCurrent( |
| 949 | mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
| 950 | mEgl.eglDestroySurface(mEglDisplay, tmpSurface); |
| 951 | |
| 952 | if (frame.width() > maxSize[0] || frame.height() > maxSize[0]) { |
| 953 | mEgl.eglDestroyContext(mEglDisplay, mEglContext); |
| 954 | mEgl.eglTerminate(mEglDisplay); |
| 955 | Log.e(GL_LOG_TAG, "requested texture size " + frame.width() + "x" + frame.height() |
| 956 | + " exceeds the support maximum of " + maxSize[0] + "x" + maxSize[0]); |
| 957 | return false; |
| 958 | } |
| 959 | |
| 960 | mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null); |
| 961 | if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) { |
| 962 | int error = mEgl.eglGetError(); |
| 963 | if (error == EGL10.EGL_BAD_NATIVE_WINDOW || error == EGL10.EGL_BAD_ALLOC) { |
| 964 | Log.e(GL_LOG_TAG, "createWindowSurface returned " + GLUtils.getEGLErrorString(error) |
| 965 | + "."); |
| 966 | return false; |
| 967 | } |
| 968 | throw new RuntimeException("createWindowSurface failed " |
| 969 | + GLUtils.getEGLErrorString(error)); |
| 970 | } |
| 971 | |
| 972 | if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { |
| 973 | throw new RuntimeException("eglMakeCurrent failed " |
| 974 | + GLUtils.getEGLErrorString(mEgl.eglGetError())); |
| 975 | } |
| 976 | |
| 977 | mProgram = buildProgram(S_SIMPLE_VS, S_SIMPLE_FS); |
| 978 | if (mBackground != null) { |
| 979 | mTexture = loadTexture(mBackground); |
| 980 | } |
| 981 | |
| 982 | return true; |
| 983 | } |
| 984 | |
| 985 | |
| 986 | EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { |
| 987 | int[] attribList = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE}; |
| 988 | return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attribList); |
| 989 | } |
| 990 | |
| 991 | private EGLConfig chooseEglConfig() { |
| 992 | int[] configsCount = new int[1]; |
| 993 | EGLConfig[] configs = new EGLConfig[1]; |
| 994 | int[] configSpec = getConfig(); |
| 995 | if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) { |
| 996 | throw new IllegalArgumentException("eglChooseConfig failed " |
| 997 | + GLUtils.getEGLErrorString(mEgl.eglGetError())); |
| 998 | } else if (configsCount[0] > 0) { |
| 999 | return configs[0]; |
| 1000 | } |
| 1001 | return null; |
| 1002 | } |
| 1003 | |
| 1004 | private int[] getConfig() { |
| 1005 | return new int[]{ |
| 1006 | EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, |
| 1007 | EGL10.EGL_RED_SIZE, 8, |
| 1008 | EGL10.EGL_GREEN_SIZE, 8, |
| 1009 | EGL10.EGL_BLUE_SIZE, 8, |
| 1010 | EGL10.EGL_ALPHA_SIZE, 0, |
| 1011 | EGL10.EGL_DEPTH_SIZE, 0, |
| 1012 | EGL10.EGL_STENCIL_SIZE, 0, |
| 1013 | EGL10.EGL_CONFIG_CAVEAT, EGL10.EGL_NONE, |
| 1014 | EGL10.EGL_NONE |
| 1015 | }; |
| 1016 | } |
| 1017 | } |
| 1018 | } |