Merge "Remove GLSurfaceView and release EGL context." into qt-dev

This commit is contained in:
TreeHugger Robot
2019-05-21 06:55:46 +00:00
committed by Android (Google) Code Review
6 changed files with 551 additions and 225 deletions

View File

@@ -16,13 +16,22 @@
package com.android.systemui;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Rect;
import android.opengl.GLSurfaceView;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.util.Size;
import android.view.SurfaceHolder;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.glwallpaper.EglHelper;
import com.android.systemui.glwallpaper.GLWallpaperRenderer;
import com.android.systemui.glwallpaper.ImageWallpaperRenderer;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.DozeParameters;
/**
* Default built-in wallpaper that simply shows a static image.
@@ -30,112 +39,183 @@ import com.android.systemui.glwallpaper.ImageWallpaperRenderer;
@SuppressWarnings({"UnusedDeclaration"})
public class ImageWallpaper extends WallpaperService {
private static final String TAG = ImageWallpaper.class.getSimpleName();
// We delayed destroy render context that subsequent render requests have chance to cancel it.
// This is to avoid destroying then recreating render context in a very short time.
private static final int DELAY_FINISH_RENDERING = 1000;
@Override
public Engine onCreateEngine() {
return new GLEngine(this);
}
class GLEngine extends Engine {
private GLWallpaperSurfaceView mWallpaperSurfaceView;
class GLEngine extends Engine implements GLWallpaperRenderer.SurfaceProxy, StateListener {
// Surface is rejected if size below a threshold on some devices (ie. 8px on elfin)
// set min to 64 px (CTS covers this), please refer to ag/4867989 for detail.
@VisibleForTesting
static final int MIN_SURFACE_WIDTH = 64;
@VisibleForTesting
static final int MIN_SURFACE_HEIGHT = 64;
private GLWallpaperRenderer mRenderer;
private EglHelper mEglHelper;
private StatusBarStateController mController;
private final Runnable mFinishRenderingTask = this::finishRendering;
private final boolean mNeedTransition;
private boolean mNeedRedraw;
GLEngine(Context context) {
mWallpaperSurfaceView = new GLWallpaperSurfaceView(context);
mWallpaperSurfaceView.setRenderer(
new ImageWallpaperRenderer(context, mWallpaperSurfaceView));
mWallpaperSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
setOffsetNotificationsEnabled(true);
mNeedTransition = ActivityManager.isHighEndGfx()
&& !DozeParameters.getInstance(context).getDisplayNeedsBlanking();
// We will preserve EGL context when we are in lock screen or aod
// to avoid janking in following transition, we need to release when back to home.
mController = Dependency.get(StatusBarStateController.class);
if (mController != null) {
mController.addCallback(this /* StateListener */);
}
mEglHelper = new EglHelper();
mRenderer = new ImageWallpaperRenderer(context, this /* SurfaceProxy */);
}
@Override
public void onCreate(SurfaceHolder surfaceHolder) {
setFixedSizeAllowed(true);
setOffsetNotificationsEnabled(false);
updateSurfaceSize();
}
private void updateSurfaceSize() {
SurfaceHolder holder = getSurfaceHolder();
Size frameSize = mRenderer.reportSurfaceSize();
int width = Math.max(MIN_SURFACE_WIDTH, frameSize.getWidth());
int height = Math.max(MIN_SURFACE_HEIGHT, frameSize.getHeight());
holder.setFixedSize(width, height);
}
@Override
public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
if (mWallpaperSurfaceView != null) {
mWallpaperSurfaceView.notifyAmbientModeChanged(inAmbientMode, animationDuration);
}
}
@Override
public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep,
float yOffsetStep, int xPixelOffset, int yPixelOffset) {
if (mWallpaperSurfaceView != null) {
mWallpaperSurfaceView.notifyOffsetsChanged(xOffset, yOffset);
}
mRenderer.updateAmbientMode(inAmbientMode,
(mNeedTransition || animationDuration != 0) ? animationDuration : 0);
}
@Override
public void onDestroy() {
if (mWallpaperSurfaceView != null) {
mWallpaperSurfaceView.onPause();
if (mController != null) {
mController.removeCallback(this /* StateListener */);
}
mController = null;
mRenderer.finish();
mRenderer = null;
mEglHelper.finish();
mEglHelper = null;
getSurfaceHolder().getSurface().hwuiDestroy();
}
private class GLWallpaperSurfaceView extends GLSurfaceView implements ImageGLView {
private WallpaperStatusListener mWallpaperStatusListener;
@Override
public void onSurfaceCreated(SurfaceHolder holder) {
mEglHelper.init(holder);
mRenderer.onSurfaceCreated();
}
GLWallpaperSurfaceView(Context context) {
super(context);
setEGLContextClientVersion(2);
}
@Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mRenderer.onSurfaceChanged(width, height);
mNeedRedraw = true;
}
@Override
public SurfaceHolder getHolder() {
return getSurfaceHolder();
}
@Override
public void setRenderer(Renderer renderer) {
super.setRenderer(renderer);
mWallpaperStatusListener = (WallpaperStatusListener) renderer;
}
private void notifyAmbientModeChanged(boolean inAmbient, long duration) {
if (mWallpaperStatusListener != null) {
mWallpaperStatusListener.onAmbientModeChanged(inAmbient, duration);
}
}
private void notifyOffsetsChanged(float xOffset, float yOffset) {
if (mWallpaperStatusListener != null) {
mWallpaperStatusListener.onOffsetsChanged(
xOffset, yOffset, getHolder().getSurfaceFrame());
}
}
@Override
public void render() {
@Override
public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
if (mNeedRedraw) {
preRender();
requestRender();
postRender();
mNeedRedraw = false;
}
}
}
/**
* A listener to trace status of image wallpaper.
*/
public interface WallpaperStatusListener {
@Override
public SurfaceHolder getHolder() {
return getSurfaceHolder();
}
/**
* Called back while ambient mode changes.
* @param inAmbientMode true if is in ambient mode, false otherwise.
* @param duration the duration of animation.
*/
void onAmbientModeChanged(boolean inAmbientMode, long duration);
@Override
public void onStatePostChange() {
// When back to home, we try to release EGL, which is preserved in lock screen or aod.
if (mController.getState() == StatusBarState.SHADE) {
scheduleFinishRendering();
}
}
/**
* Called back while wallpaper offsets.
* @param xOffset The offset portion along x.
* @param yOffset The offset portion along y.
*/
void onOffsetsChanged(float xOffset, float yOffset, Rect frame);
}
@Override
public void preRender() {
boolean contextRecreated = false;
Rect frame = getSurfaceHolder().getSurfaceFrame();
getMainThreadHandler().removeCallbacks(mFinishRenderingTask);
/**
* An abstraction for view of GLRenderer.
*/
public interface ImageGLView {
// Check if we need to recreate egl context.
if (!mEglHelper.hasEglContext()) {
mEglHelper.destroyEglSurface();
if (!mEglHelper.createEglContext()) {
Log.w(TAG, "recreate egl context failed!");
} else {
contextRecreated = true;
}
}
/**
* Ask the view to render.
*/
void render();
// Check if we need to recreate egl surface.
if (mEglHelper.hasEglContext() && !mEglHelper.hasEglSurface()) {
if (!mEglHelper.createEglSurface(getSurfaceHolder())) {
Log.w(TAG, "recreate egl surface failed!");
}
}
// If we recreate egl context, notify renderer to setup again.
if (mEglHelper.hasEglContext() && mEglHelper.hasEglSurface() && contextRecreated) {
mRenderer.onSurfaceCreated();
mRenderer.onSurfaceChanged(frame.width(), frame.height());
}
}
@Override
public void requestRender() {
Rect frame = getSurfaceHolder().getSurfaceFrame();
boolean readyToRender = mEglHelper.hasEglContext() && mEglHelper.hasEglSurface()
&& frame.width() > 0 && frame.height() > 0;
if (readyToRender) {
mRenderer.onDrawFrame();
if (!mEglHelper.swapBuffer()) {
Log.e(TAG, "drawFrame failed!");
}
} else {
Log.e(TAG, "requestRender: not ready, has context=" + mEglHelper.hasEglContext()
+ ", has surface=" + mEglHelper.hasEglSurface()
+ ", frame=" + frame);
}
}
@Override
public void postRender() {
scheduleFinishRendering();
}
private void scheduleFinishRendering() {
getMainThreadHandler().removeCallbacks(mFinishRenderingTask);
getMainThreadHandler().postDelayed(mFinishRenderingTask, DELAY_FINISH_RENDERING);
}
private void finishRendering() {
if (mEglHelper != null) {
mEglHelper.destroyEglSurface();
if (!needPreserveEglContext()) {
mEglHelper.destroyEglContext();
}
}
}
private boolean needPreserveEglContext() {
return mNeedTransition && mController != null
&& mController.getState() == StatusBarState.KEYGUARD;
}
}
}

View File

@@ -0,0 +1,230 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.glwallpaper;
import static android.opengl.EGL14.EGL_ALPHA_SIZE;
import static android.opengl.EGL14.EGL_BLUE_SIZE;
import static android.opengl.EGL14.EGL_CONFIG_CAVEAT;
import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION;
import static android.opengl.EGL14.EGL_DEFAULT_DISPLAY;
import static android.opengl.EGL14.EGL_DEPTH_SIZE;
import static android.opengl.EGL14.EGL_GREEN_SIZE;
import static android.opengl.EGL14.EGL_NONE;
import static android.opengl.EGL14.EGL_NO_CONTEXT;
import static android.opengl.EGL14.EGL_NO_DISPLAY;
import static android.opengl.EGL14.EGL_NO_SURFACE;
import static android.opengl.EGL14.EGL_OPENGL_ES2_BIT;
import static android.opengl.EGL14.EGL_RED_SIZE;
import static android.opengl.EGL14.EGL_RENDERABLE_TYPE;
import static android.opengl.EGL14.EGL_STENCIL_SIZE;
import static android.opengl.EGL14.EGL_SUCCESS;
import static android.opengl.EGL14.eglChooseConfig;
import static android.opengl.EGL14.eglCreateContext;
import static android.opengl.EGL14.eglCreateWindowSurface;
import static android.opengl.EGL14.eglDestroyContext;
import static android.opengl.EGL14.eglDestroySurface;
import static android.opengl.EGL14.eglGetDisplay;
import static android.opengl.EGL14.eglGetError;
import static android.opengl.EGL14.eglInitialize;
import static android.opengl.EGL14.eglMakeCurrent;
import static android.opengl.EGL14.eglSwapBuffers;
import static android.opengl.EGL14.eglTerminate;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.opengl.GLUtils;
import android.util.Log;
import android.view.SurfaceHolder;
/**
* A helper class to handle EGL management.
*/
public class EglHelper {
private static final String TAG = EglHelper.class.getSimpleName();
private EGLDisplay mEglDisplay;
private EGLConfig mEglConfig;
private EGLContext mEglContext;
private EGLSurface mEglSurface;
/**
* Initialize EGL and prepare EglSurface.
* @param surfaceHolder surface holder.
* @return true if EglSurface is ready.
*/
public boolean init(SurfaceHolder surfaceHolder) {
mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (mEglDisplay == EGL_NO_DISPLAY) {
Log.w(TAG, "eglGetDisplay failed: " + GLUtils.getEGLErrorString(eglGetError()));
return false;
}
if (!eglInitialize(mEglDisplay, null, 0, null, 0)) {
Log.w(TAG, "eglInitialize failed: " + GLUtils.getEGLErrorString(eglGetError()));
return false;
}
mEglConfig = chooseEglConfig();
if (mEglConfig == null) {
Log.w(TAG, "eglConfig not initialized!");
return false;
}
if (!createEglContext()) {
Log.w(TAG, "Can't create EGLContext!");
return false;
}
if (!createEglSurface(surfaceHolder)) {
Log.w(TAG, "Can't create EGLSurface!");
return false;
}
return true;
}
private EGLConfig chooseEglConfig() {
int[] configsCount = new int[1];
EGLConfig[] configs = new EGLConfig[1];
int[] configSpec = getConfig();
if (!eglChooseConfig(mEglDisplay, configSpec, 0, configs, 0, 1, configsCount, 0)) {
Log.w(TAG, "eglChooseConfig failed: " + GLUtils.getEGLErrorString(eglGetError()));
return null;
} else {
if (configsCount[0] <= 0) {
Log.w(TAG, "eglChooseConfig failed, invalid configs count: " + configsCount[0]);
return null;
} else {
return configs[0];
}
}
}
private int[] getConfig() {
return new int[] {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 0,
EGL_STENCIL_SIZE, 0,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_CONFIG_CAVEAT, EGL_NONE,
EGL_NONE
};
}
/**
* Prepare an EglSurface.
* @param surfaceHolder surface holder.
* @return true if EglSurface is ready.
*/
public boolean createEglSurface(SurfaceHolder surfaceHolder) {
mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null, 0);
if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
Log.w(TAG, "createWindowSurface failed: " + GLUtils.getEGLErrorString(eglGetError()));
return false;
}
if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
Log.w(TAG, "eglMakeCurrent failed: " + GLUtils.getEGLErrorString(eglGetError()));
return false;
}
return true;
}
/**
* Destroy EglSurface.
*/
public void destroyEglSurface() {
if (hasEglSurface()) {
eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface(mEglDisplay, mEglSurface);
mEglSurface = null;
}
}
/**
* Check if we have a valid EglSurface.
* @return true if EglSurface is ready.
*/
public boolean hasEglSurface() {
return mEglSurface != null && mEglSurface != EGL_NO_SURFACE;
}
/**
* Prepare EglContext.
* @return true if EglContext is ready.
*/
public boolean createEglContext() {
int[] attrib_list = new int[] {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attrib_list, 0);
if (mEglContext == EGL_NO_CONTEXT) {
Log.w(TAG, "eglCreateContext failed: " + GLUtils.getEGLErrorString(eglGetError()));
return false;
}
return true;
}
/**
* Destroy EglContext.
*/
public void destroyEglContext() {
if (hasEglContext()) {
eglDestroyContext(mEglDisplay, mEglContext);
mEglContext = null;
}
}
/**
* Check if we have EglContext.
* @return true if EglContext is ready.
*/
public boolean hasEglContext() {
return mEglContext != null;
}
/**
* Swap buffer to display.
* @return true if swap successfully.
*/
public boolean swapBuffer() {
boolean status = eglSwapBuffers(mEglDisplay, mEglSurface);
int error = eglGetError();
if (error != EGL_SUCCESS) {
Log.w(TAG, "eglSwapBuffers failed: " + GLUtils.getEGLErrorString(error));
}
return status;
}
/**
* Destroy EglSurface and EglContext, then terminate EGL.
*/
public void finish() {
if (hasEglSurface()) {
destroyEglSurface();
}
if (hasEglContext()) {
destroyEglContext();
}
eglTerminate(mEglDisplay);
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.glwallpaper;
import android.util.Size;
import android.view.SurfaceHolder;
/**
* A renderer which is responsible for making OpenGL calls to render a frame.
*/
public interface GLWallpaperRenderer {
/**
* Called when the surface is created or recreated.
*/
void onSurfaceCreated();
/**
* Called when the surface changed size.
* @param width surface width.
* @param height surface height.
*/
void onSurfaceChanged(int width, int height);
/**
* Called to draw the current frame.
*/
void onDrawFrame();
/**
* Notify ambient mode is changed.
* @param inAmbientMode true if in ambient mode.
* @param duration duration of transition.
*/
void updateAmbientMode(boolean inAmbientMode, long duration);
/**
* Ask renderer to report the surface size it needs.
*/
Size reportSurfaceSize();
/**
* Called when no need to render any more.
*/
void finish();
/**
* A proxy which owns surface holder.
*/
interface SurfaceProxy {
/**
* Get surface holder.
* @return surface holder.
*/
SurfaceHolder getHolder();
/**
* Ask proxy to start rendering frame to surface.
*/
void requestRender();
/**
* Ask proxy to prepare render context.
*/
void preRender();
/**
* Ask proxy to destroy render context.
*/
void postRender();
}
}

View File

@@ -195,76 +195,4 @@ class ImageGLWallpaper {
glUniform1i(mUniTexture, 0);
}
/**
* This method adjust s(x-axis), t(y-axis) texture coordinates
* to prevent the wallpaper from being stretched.
* The adjustment happens if either the width or height of the bitmap is larger than
* corresponding size of the surface.
* If both width and height are larger than corresponding size of the surface,
* the adjustment will happen at both s, t side.
*
* @param bitmapWidth The width of the bitmap.
* @param bitmapHeight The height of the bitmap.
* @param surfaceWidth The width of the surface.
* @param surfaceHeight The height of the surface.
* @param xOffset The offset amount along s axis.
* @param yOffset The offset amount along t axis.
*/
void adjustTextureCoordinates(int bitmapWidth, int bitmapHeight,
int surfaceWidth, int surfaceHeight, float xOffset, float yOffset) {
float[] coordinates = TEXTURES.clone();
if (bitmapWidth > surfaceWidth) {
// Calculate the new s pos in pixels.
float pixelS = (float) Math.round((bitmapWidth - surfaceWidth) * xOffset);
// Calculate the s pos in texture coordinate.
float coordinateS = pixelS / bitmapWidth;
// Calculate the percentage occupied by the surface width in bitmap width.
float surfacePercentageW = (float) surfaceWidth / bitmapWidth;
// Need also consider the case if bitmap height is smaller than surface height.
if (bitmapHeight < surfaceHeight) {
// We will narrow the surface percentage to keep aspect ratio.
surfacePercentageW *= (float) bitmapHeight / surfaceHeight;
}
// Determine the final s pos, also limit the legal s pos to prevent from out of range.
float s = coordinateS + surfacePercentageW > 1f ? 1f - surfacePercentageW : coordinateS;
// Traverse the s pos in texture coordinates array and adjust the s pos accordingly.
for (int i = 0; i < coordinates.length; i += 2) {
// indices 2, 4 and 6 are the end of s coordinates.
if (i == 2 || i == 4 || i == 6) {
coordinates[i] = Math.min(1f, s + surfacePercentageW);
} else {
coordinates[i] = s;
}
}
}
if (bitmapHeight > surfaceHeight) {
// Calculate the new t pos in pixels.
float pixelT = (float) Math.round((bitmapHeight - surfaceHeight) * yOffset);
// Calculate the t pos in texture coordinate.
float coordinateT = pixelT / bitmapHeight;
// Calculate the percentage occupied by the surface height in bitmap height.
float surfacePercentageH = (float) surfaceHeight / bitmapHeight;
// Need also consider the case if bitmap width is smaller than surface width.
if (bitmapWidth < surfaceWidth) {
// We will narrow the surface percentage to keep aspect ratio.
surfacePercentageH *= (float) bitmapWidth / surfaceWidth;
}
// Determine the final t pos, also limit the legal t pos to prevent from out of range.
float t = coordinateT + surfacePercentageH > 1f ? 1f - surfacePercentageH : coordinateT;
// Traverse the t pos in texture coordinates array and adjust the t pos accordingly.
for (int i = 1; i < coordinates.length; i += 2) {
// indices 1, 3 and 11 are the end of t coordinates.
if (i == 1 || i == 3 || i == 11) {
coordinates[i] = Math.min(1f, t + surfacePercentageH);
} else {
coordinates[i] = t;
}
}
}
mTextureBuffer.put(coordinates);
mTextureBuffer.position(0);
}
}

View File

@@ -56,11 +56,18 @@ class ImageRevealHelper {
@Override
public void onAnimationEnd(Animator animation) {
if (!mIsCanceled) {
mAwake = !mAwake;
if (!mIsCanceled && mRevealListener != null) {
mRevealListener.onRevealEnd();
}
mIsCanceled = false;
}
@Override
public void onAnimationStart(Animator animation) {
if (mRevealListener != null) {
mRevealListener.onRevealStart();
}
}
});
}
@@ -74,10 +81,6 @@ class ImageRevealHelper {
return mReveal;
}
public boolean isAwake() {
return mAwake;
}
void updateAwake(boolean awake, long duration) {
mAwake = awake;
mAnimator.setDuration(duration);
@@ -93,5 +96,15 @@ class ImageRevealHelper {
* Called back while reveal status changes.
*/
void onRevealStateChanged();
/**
* Called back while reveal starts.
*/
void onRevealStart();
/**
* Called back while reveal ends.
*/
void onRevealEnd();
}
}

View File

@@ -26,23 +26,17 @@ import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.opengl.GLSurfaceView;
import android.os.Build;
import android.util.Log;
import android.util.MathUtils;
import android.util.Size;
import com.android.systemui.ImageWallpaper;
import com.android.systemui.ImageWallpaper.ImageGLView;
import com.android.systemui.R;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
/**
* A GL renderer for image wallpaper.
*/
public class ImageWallpaperRenderer implements GLSurfaceView.Renderer,
ImageWallpaper.WallpaperStatusListener, ImageRevealHelper.RevealStateListener {
public class ImageWallpaperRenderer implements GLWallpaperRenderer,
ImageRevealHelper.RevealStateListener {
private static final String TAG = ImageWallpaperRenderer.class.getSimpleName();
private static final float SCALE_VIEWPORT_MIN = 0.98f;
private static final float SCALE_VIEWPORT_MAX = 1f;
@@ -52,62 +46,61 @@ public class ImageWallpaperRenderer implements GLSurfaceView.Renderer,
private final ImageGLWallpaper mWallpaper;
private final ImageProcessHelper mImageProcessHelper;
private final ImageRevealHelper mImageRevealHelper;
private final ImageGLView mGLView;
private float mXOffset = 0f;
private float mYOffset = 0f;
private int mWidth = 0;
private int mHeight = 0;
private SurfaceProxy mProxy;
private Rect mSurfaceSize;
private Bitmap mBitmap;
private int mBitmapWidth = 0;
private int mBitmapHeight = 0;
public ImageWallpaperRenderer(Context context, ImageGLView glView) {
public ImageWallpaperRenderer(Context context, SurfaceProxy proxy) {
mWallpaperManager = context.getSystemService(WallpaperManager.class);
if (mWallpaperManager == null) {
Log.w(TAG, "WallpaperManager not available");
}
mProxy = proxy;
mProgram = new ImageGLProgram(context);
mWallpaper = new ImageGLWallpaper(mProgram);
mImageProcessHelper = new ImageProcessHelper();
mImageRevealHelper = new ImageRevealHelper(this);
mGLView = glView;
if (mWallpaperManager != null) {
mBitmap = mWallpaperManager.getBitmap();
mBitmapWidth = mBitmap.getWidth();
mBitmapHeight = mBitmap.getHeight();
if (loadBitmap()) {
// Compute threshold of the image, this is an async work.
mImageProcessHelper.start(mBitmap);
mWallpaperManager.forgetLoadedWallpaper();
}
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
public void onSurfaceCreated() {
glClearColor(0f, 0f, 0f, 1.0f);
mProgram.useGLProgram(
R.raw.image_wallpaper_vertex_shader, R.raw.image_wallpaper_fragment_shader);
if (!loadBitmap()) {
Log.w(TAG, "reload bitmap failed!");
}
mWallpaper.setup(mBitmap);
mBitmap = null;
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
glViewport(0, 0, width, height);
if (Build.IS_DEBUGGABLE) {
Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height
+ ", xOffset=" + mXOffset + ", yOffset=" + mYOffset);
private boolean loadBitmap() {
if (mWallpaperManager != null && mBitmap == null) {
mBitmap = mWallpaperManager.getBitmap();
mWallpaperManager.forgetLoadedWallpaper();
if (mBitmap != null) {
mSurfaceSize = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
}
}
mWidth = width;
mHeight = height;
mWallpaper.adjustTextureCoordinates(
mBitmapWidth, mBitmapHeight, width, height, mXOffset, mYOffset);
return mBitmap != null;
}
@Override
public void onDrawFrame(GL10 gl) {
public void onSurfaceChanged(int width, int height) {
glViewport(0, 0, width, height);
}
@Override
public void onDrawFrame() {
float threshold = mImageProcessHelper.getThreshold();
float reveal = mImageRevealHelper.getReveal();
@@ -122,50 +115,45 @@ public class ImageWallpaperRenderer implements GLSurfaceView.Renderer,
mWallpaper.draw();
}
@Override
public void updateAmbientMode(boolean inAmbientMode, long duration) {
mImageRevealHelper.updateAwake(!inAmbientMode, duration);
}
@Override
public Size reportSurfaceSize() {
return new Size(mSurfaceSize.width(), mSurfaceSize.height());
}
@Override
public void finish() {
mProxy = null;
}
private void scaleViewport(float reveal) {
int width = mSurfaceSize.width();
int height = mSurfaceSize.height();
// Interpolation between SCALE_VIEWPORT_MAX and SCALE_VIEWPORT_MIN by reveal.
float vpScaled = MathUtils.lerp(SCALE_VIEWPORT_MAX, SCALE_VIEWPORT_MIN, reveal);
// Calculate the offset amount from the lower left corner.
float offset = (SCALE_VIEWPORT_MAX - vpScaled) / 2;
// Change the viewport.
glViewport((int) (mWidth * offset), (int) (mHeight * offset),
(int) (mWidth * vpScaled), (int) (mHeight * vpScaled));
}
@Override
public void onAmbientModeChanged(boolean inAmbientMode, long duration) {
mImageRevealHelper.updateAwake(!inAmbientMode, duration);
requestRender();
}
@Override
public void onOffsetsChanged(float xOffset, float yOffset, Rect frame) {
if (frame == null || (xOffset == mXOffset && yOffset == mYOffset)) {
return;
}
int width = frame.width();
int height = frame.height();
mXOffset = xOffset;
mYOffset = yOffset;
if (Build.IS_DEBUGGABLE) {
Log.d(TAG, "onOffsetsChanged: width=" + width + ", height=" + height
+ ", xOffset=" + mXOffset + ", yOffset=" + mYOffset);
}
mWallpaper.adjustTextureCoordinates(
mBitmapWidth, mBitmapHeight, width, height, mXOffset, mYOffset);
requestRender();
glViewport((int) (width * offset), (int) (height * offset),
(int) (width * vpScaled), (int) (height * vpScaled));
}
@Override
public void onRevealStateChanged() {
requestRender();
mProxy.requestRender();
}
private void requestRender() {
if (mGLView != null) {
mGLView.render();
}
@Override
public void onRevealStart() {
mProxy.preRender();
}
@Override
public void onRevealEnd() {
mProxy.postRender();
}
}