Merge "Remove GLSurfaceView and release EGL context." into qt-dev
am: 2d6f9aa60a
Change-Id: I80219d469696400a0a93f3be245a5456f979b083
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user