Merge "Remove unnecessary transition logic from ImageWallpaper" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
31b94bfc1e
@@ -3,74 +3,9 @@ precision mediump float;
|
||||
// The actual wallpaper texture.
|
||||
uniform sampler2D uTexture;
|
||||
|
||||
// The 85th percenile for the luminance histogram of the image (a value between 0 and 1).
|
||||
// This value represents the point in histogram that includes 85% of the pixels of the image.
|
||||
uniform float uPer85;
|
||||
|
||||
// Reveal is the animation value that goes from 1 (the image is hidden) to 0 (the image is visible).
|
||||
uniform float uReveal;
|
||||
|
||||
// The opacity of locked screen (constant value).
|
||||
uniform float uAod2Opacity;
|
||||
varying vec2 vTextureCoordinates;
|
||||
|
||||
/*
|
||||
* Calculates the relative luminance of the pixel.
|
||||
*/
|
||||
vec3 luminosity(vec3 color) {
|
||||
float lum = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
|
||||
return vec3(lum);
|
||||
}
|
||||
|
||||
vec4 transform(vec3 diffuse) {
|
||||
// Getting the luminance for this pixel
|
||||
vec3 lum = luminosity(diffuse);
|
||||
|
||||
/*
|
||||
* while the reveal > per85, it shows the luminance image (B&W image)
|
||||
* then when moving passed that value, the image gets colored.
|
||||
*/
|
||||
float trans = smoothstep(0., uPer85, uReveal);
|
||||
diffuse = mix(diffuse, lum, trans);
|
||||
|
||||
// 'lower' value represents the capped 'reveal' value to the range [0, per85]
|
||||
float selector = step(uPer85, uReveal);
|
||||
float lower = mix(uReveal, uPer85, selector);
|
||||
|
||||
/*
|
||||
* Remaps image:
|
||||
* - from reveal=1 to reveal=per85 => lower=per85, diffuse=luminance
|
||||
* That means that remaps black and white image pixel
|
||||
* from a possible values of [0,1] to [per85, 1] (if the pixel is darker than per85,
|
||||
* it's gonna be black, if it's between per85 and 1, it's gonna be gray
|
||||
* and if it's 1 it's gonna be white).
|
||||
* - from reveal=per85 to reveal=0 => lower=reveal, 'diffuse' changes from luminance to color
|
||||
* That means that remaps each image pixel color (rgb)
|
||||
* from a possible values of [0,1] to [lower, 1] (if the pixel color is darker than 'lower',
|
||||
* it's gonna be 0, if it's between 'lower' and 1, it's gonna be remap to a value
|
||||
* between 0 and 1 and if it's 1 it's gonna be 1).
|
||||
* - if reveal=0 => lower=0, diffuse=color image
|
||||
* The image is shown as it is, colored.
|
||||
*/
|
||||
vec3 remaps = smoothstep(lower, 1., diffuse);
|
||||
|
||||
// Interpolate between diffuse and remaps using reveal to avoid over saturation.
|
||||
diffuse = mix(diffuse, remaps, uReveal);
|
||||
|
||||
/*
|
||||
* Fades in the pixel value:
|
||||
* - if reveal=1 => fadeInOpacity=0
|
||||
* - from reveal=1 to reveal=per85 => 0<=fadeInOpacity<=1
|
||||
* - if reveal>per85 => fadeInOpacity=1
|
||||
*/
|
||||
float fadeInOpacity = 1. - smoothstep(uPer85, 1., uReveal);
|
||||
diffuse *= uAod2Opacity * fadeInOpacity;
|
||||
|
||||
return vec4(diffuse.r, diffuse.g, diffuse.b, 1.);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// gets the pixel value of the wallpaper for this uv coordinates on screen.
|
||||
vec4 fragColor = texture2D(uTexture, vTextureCoordinates);
|
||||
gl_FragColor = transform(fragColor.rgb);
|
||||
gl_FragColor = texture2D(uTexture, vTextureCoordinates);
|
||||
}
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
package com.android.systemui;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
@@ -27,17 +24,12 @@ import android.os.Trace;
|
||||
import android.service.wallpaper.WallpaperService;
|
||||
import android.util.Log;
|
||||
import android.util.Size;
|
||||
import android.view.DisplayInfo;
|
||||
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;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
@@ -53,16 +45,12 @@ public class ImageWallpaper extends WallpaperService {
|
||||
// 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;
|
||||
private static final int INTERVAL_WAIT_FOR_RENDERING = 100;
|
||||
private static final int PATIENCE_WAIT_FOR_RENDERING = 10;
|
||||
private static final boolean DEBUG = true;
|
||||
private final DozeParameters mDozeParameters;
|
||||
private static final boolean DEBUG = false;
|
||||
private HandlerThread mWorker;
|
||||
|
||||
@Inject
|
||||
public ImageWallpaper(DozeParameters dozeParameters) {
|
||||
public ImageWallpaper() {
|
||||
super();
|
||||
mDozeParameters = dozeParameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,7 +62,7 @@ public class ImageWallpaper extends WallpaperService {
|
||||
|
||||
@Override
|
||||
public Engine onCreateEngine() {
|
||||
return new GLEngine(this, mDozeParameters);
|
||||
return new GLEngine();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -84,7 +72,7 @@ public class ImageWallpaper extends WallpaperService {
|
||||
mWorker = null;
|
||||
}
|
||||
|
||||
class GLEngine extends Engine implements GLWallpaperRenderer.SurfaceProxy, StateListener {
|
||||
class GLEngine extends Engine {
|
||||
// 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
|
||||
@@ -94,40 +82,15 @@ public class ImageWallpaper extends WallpaperService {
|
||||
|
||||
private GLWallpaperRenderer mRenderer;
|
||||
private EglHelper mEglHelper;
|
||||
private StatusBarStateController mController;
|
||||
private final Runnable mFinishRenderingTask = this::finishRendering;
|
||||
private boolean mShouldStopTransition;
|
||||
private final DisplayInfo mDisplayInfo = new DisplayInfo();
|
||||
private final Object mMonitor = new Object();
|
||||
@VisibleForTesting
|
||||
boolean mIsHighEndGfx;
|
||||
private boolean mDisplayNeedsBlanking;
|
||||
private boolean mNeedTransition;
|
||||
private boolean mNeedRedraw;
|
||||
// This variable can only be accessed in synchronized block.
|
||||
private boolean mWaitingForRendering;
|
||||
|
||||
GLEngine(Context context, DozeParameters dozeParameters) {
|
||||
init(dozeParameters);
|
||||
GLEngine() {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
GLEngine(DozeParameters dozeParameters, Handler handler) {
|
||||
GLEngine(Handler handler) {
|
||||
super(SystemClock::elapsedRealtime, handler);
|
||||
init(dozeParameters);
|
||||
}
|
||||
|
||||
private void init(DozeParameters dozeParameters) {
|
||||
mIsHighEndGfx = ActivityManager.isHighEndGfx();
|
||||
mDisplayNeedsBlanking = dozeParameters.getDisplayNeedsBlanking();
|
||||
mNeedTransition = false;
|
||||
|
||||
// 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 */);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -135,9 +98,8 @@ public class ImageWallpaper extends WallpaperService {
|
||||
mEglHelper = getEglHelperInstance();
|
||||
// Deferred init renderer because we need to get wallpaper by display context.
|
||||
mRenderer = getRendererInstance();
|
||||
getDisplayContext().getDisplay().getDisplayInfo(mDisplayInfo);
|
||||
setFixedSizeAllowed(true);
|
||||
setOffsetNotificationsEnabled(mNeedTransition);
|
||||
setOffsetNotificationsEnabled(false);
|
||||
updateSurfaceSize();
|
||||
}
|
||||
|
||||
@@ -146,7 +108,7 @@ public class ImageWallpaper extends WallpaperService {
|
||||
}
|
||||
|
||||
ImageWallpaperRenderer getRendererInstance() {
|
||||
return new ImageWallpaperRenderer(getDisplayContext(), this /* SurfaceProxy */);
|
||||
return new ImageWallpaperRenderer(getDisplayContext());
|
||||
}
|
||||
|
||||
private void updateSurfaceSize() {
|
||||
@@ -157,79 +119,13 @@ public class ImageWallpaper extends WallpaperService {
|
||||
holder.setFixedSize(width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if necessary to stop transition with current wallpaper on this device. <br/>
|
||||
* This should only be invoked after {@link #onSurfaceCreated(SurfaceHolder)}}
|
||||
* is invoked since it needs display context and surface frame size.
|
||||
* @return true if need to stop transition.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean checkIfShouldStopTransition() {
|
||||
int orientation = getDisplayContext().getResources().getConfiguration().orientation;
|
||||
Rect frame = getSurfaceHolder().getSurfaceFrame();
|
||||
Rect display = new Rect();
|
||||
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
display.set(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
|
||||
} else {
|
||||
display.set(0, 0, mDisplayInfo.logicalHeight, mDisplayInfo.logicalWidth);
|
||||
}
|
||||
return mNeedTransition
|
||||
&& (frame.width() < display.width() || frame.height() < display.height());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep,
|
||||
float yOffsetStep, int xPixelOffset, int yPixelOffset) {
|
||||
if (mWorker == null) return;
|
||||
mWorker.getThreadHandler().post(() -> mRenderer.updateOffsets(xOffset, yOffset));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
|
||||
if (mWorker == null || !mNeedTransition) return;
|
||||
final long duration = mShouldStopTransition ? 0 : animationDuration;
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onAmbientModeChanged: inAmbient=" + inAmbientMode
|
||||
+ ", duration=" + duration
|
||||
+ ", mShouldStopTransition=" + mShouldStopTransition);
|
||||
}
|
||||
mWorker.getThreadHandler().post(
|
||||
() -> mRenderer.updateAmbientMode(inAmbientMode, duration));
|
||||
if (inAmbientMode && animationDuration == 0) {
|
||||
// This means that we are transiting from home to aod, to avoid
|
||||
// race condition between window visibility and transition,
|
||||
// we don't return until the transition is finished. See b/136643341.
|
||||
waitForBackgroundRendering();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldZoomOutWallpaper() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void waitForBackgroundRendering() {
|
||||
synchronized (mMonitor) {
|
||||
try {
|
||||
mWaitingForRendering = true;
|
||||
for (int patience = 1; mWaitingForRendering; patience++) {
|
||||
mMonitor.wait(INTERVAL_WAIT_FOR_RENDERING);
|
||||
mWaitingForRendering &= patience < PATIENCE_WAIT_FOR_RENDERING;
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
} finally {
|
||||
mWaitingForRendering = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (mController != null) {
|
||||
mController.removeCallback(this /* StateListener */);
|
||||
}
|
||||
mController = null;
|
||||
|
||||
mWorker.getThreadHandler().post(() -> {
|
||||
mRenderer.finish();
|
||||
mRenderer = null;
|
||||
@@ -240,7 +136,6 @@ public class ImageWallpaper extends WallpaperService {
|
||||
|
||||
@Override
|
||||
public void onSurfaceCreated(SurfaceHolder holder) {
|
||||
mShouldStopTransition = checkIfShouldStopTransition();
|
||||
if (mWorker == null) return;
|
||||
mWorker.getThreadHandler().post(() -> {
|
||||
mEglHelper.init(holder, needSupportWideColorGamut());
|
||||
@@ -251,32 +146,13 @@ public class ImageWallpaper extends WallpaperService {
|
||||
@Override
|
||||
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||
if (mWorker == null) return;
|
||||
mWorker.getThreadHandler().post(() -> {
|
||||
mRenderer.onSurfaceChanged(width, height);
|
||||
mNeedRedraw = true;
|
||||
});
|
||||
mWorker.getThreadHandler().post(() -> mRenderer.onSurfaceChanged(width, height));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
|
||||
if (mWorker == null) return;
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onSurfaceRedrawNeeded: mNeedRedraw=" + mNeedRedraw);
|
||||
}
|
||||
|
||||
mWorker.getThreadHandler().post(() -> {
|
||||
if (mNeedRedraw) {
|
||||
drawFrame();
|
||||
mNeedRedraw = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVisibilityChanged(boolean visible) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "wallpaper visibility changes: " + visible);
|
||||
}
|
||||
mWorker.getThreadHandler().post(this::drawFrame);
|
||||
}
|
||||
|
||||
private void drawFrame() {
|
||||
@@ -285,15 +161,6 @@ public class ImageWallpaper extends WallpaperService {
|
||||
postRender();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatePostChange() {
|
||||
// When back to home, we try to release EGL, which is preserved in lock screen or aod.
|
||||
if (mWorker != null && mController.getState() == StatusBarState.SHADE) {
|
||||
mWorker.getThreadHandler().post(this::scheduleFinishRendering);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRender() {
|
||||
// This method should only be invoked from worker thread.
|
||||
Trace.beginSection("ImageWallpaper#preRender");
|
||||
@@ -330,7 +197,6 @@ public class ImageWallpaper extends WallpaperService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestRender() {
|
||||
// This method should only be invoked from worker thread.
|
||||
Trace.beginSection("ImageWallpaper#requestRender");
|
||||
@@ -355,27 +221,13 @@ public class ImageWallpaper extends WallpaperService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postRender() {
|
||||
// This method should only be invoked from worker thread.
|
||||
Trace.beginSection("ImageWallpaper#postRender");
|
||||
notifyWaitingThread();
|
||||
scheduleFinishRendering();
|
||||
Trace.endSection();
|
||||
}
|
||||
|
||||
private void notifyWaitingThread() {
|
||||
synchronized (mMonitor) {
|
||||
if (mWaitingForRendering) {
|
||||
try {
|
||||
mWaitingForRendering = false;
|
||||
mMonitor.notify();
|
||||
} catch (IllegalMonitorStateException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelFinishRenderingTask() {
|
||||
if (mWorker == null) return;
|
||||
mWorker.getThreadHandler().removeCallbacks(mFinishRenderingTask);
|
||||
@@ -391,18 +243,11 @@ public class ImageWallpaper extends WallpaperService {
|
||||
Trace.beginSection("ImageWallpaper#finishRendering");
|
||||
if (mEglHelper != null) {
|
||||
mEglHelper.destroyEglSurface();
|
||||
if (!needPreserveEglContext()) {
|
||||
mEglHelper.destroyEglContext();
|
||||
}
|
||||
mEglHelper.destroyEglContext();
|
||||
}
|
||||
Trace.endSection();
|
||||
}
|
||||
|
||||
private boolean needPreserveEglContext() {
|
||||
return mNeedTransition && mController != null
|
||||
&& mController.getState() == StatusBarState.KEYGUARD;
|
||||
}
|
||||
|
||||
private boolean needSupportWideColorGamut() {
|
||||
return mRenderer.isWcgContent();
|
||||
}
|
||||
@@ -411,16 +256,6 @@ public class ImageWallpaper extends WallpaperService {
|
||||
protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
|
||||
super.dump(prefix, fd, out, args);
|
||||
out.print(prefix); out.print("Engine="); out.println(this);
|
||||
out.print(prefix); out.print("isHighEndGfx="); out.println(mIsHighEndGfx);
|
||||
out.print(prefix); out.print("displayNeedsBlanking=");
|
||||
out.println(mDisplayNeedsBlanking);
|
||||
out.print(prefix); out.print("displayInfo="); out.print(mDisplayInfo);
|
||||
out.print(prefix); out.print("mNeedTransition="); out.println(mNeedTransition);
|
||||
out.print(prefix); out.print("mShouldStopTransition=");
|
||||
out.println(mShouldStopTransition);
|
||||
out.print(prefix); out.print("StatusBarState=");
|
||||
out.println(mController != null ? mController.getState() : "null");
|
||||
|
||||
out.print(prefix); out.print("valid surface=");
|
||||
out.println(getSurfaceHolder() != null && getSurfaceHolder().getSurface() != null
|
||||
? getSurfaceHolder().getSurface().isValid()
|
||||
|
||||
@@ -48,20 +48,6 @@ public interface GLWallpaperRenderer {
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Notify the wallpaper offsets changed.
|
||||
* @param xOffset offset along x axis.
|
||||
* @param yOffset offset along y axis.
|
||||
*/
|
||||
void updateOffsets(float xOffset, float yOffset);
|
||||
|
||||
/**
|
||||
* Ask renderer to report the surface size it needs.
|
||||
*/
|
||||
@@ -81,24 +67,4 @@ public interface GLWallpaperRenderer {
|
||||
*/
|
||||
void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args);
|
||||
|
||||
/**
|
||||
* A proxy which owns surface holder.
|
||||
*/
|
||||
interface SurfaceProxy {
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ import static android.opengl.GLES20.glUniform1i;
|
||||
import static android.opengl.GLES20.glVertexAttribPointer;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Rect;
|
||||
import android.opengl.GLUtils;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -50,14 +49,9 @@ import java.nio.FloatBuffer;
|
||||
class ImageGLWallpaper {
|
||||
private static final String TAG = ImageGLWallpaper.class.getSimpleName();
|
||||
|
||||
static final String A_POSITION = "aPosition";
|
||||
static final String A_TEXTURE_COORDINATES = "aTextureCoordinates";
|
||||
static final String U_PER85 = "uPer85";
|
||||
static final String U_REVEAL = "uReveal";
|
||||
static final String U_AOD2OPACITY = "uAod2Opacity";
|
||||
static final String U_TEXTURE = "uTexture";
|
||||
|
||||
private static final int HANDLE_UNDEFINED = -1;
|
||||
private static final String A_POSITION = "aPosition";
|
||||
private static final String A_TEXTURE_COORDINATES = "aTextureCoordinates";
|
||||
private static final String U_TEXTURE = "uTexture";
|
||||
private static final int POSITION_COMPONENT_COUNT = 2;
|
||||
private static final int TEXTURE_COMPONENT_COUNT = 2;
|
||||
private static final int BYTES_PER_FLOAT = 4;
|
||||
@@ -88,14 +82,9 @@ class ImageGLWallpaper {
|
||||
|
||||
private int mAttrPosition;
|
||||
private int mAttrTextureCoordinates;
|
||||
private int mUniAod2Opacity;
|
||||
private int mUniPer85;
|
||||
private int mUniReveal;
|
||||
private int mUniTexture;
|
||||
private int mTextureId;
|
||||
|
||||
private float[] mCurrentTexCoordinate;
|
||||
|
||||
ImageGLWallpaper(ImageGLProgram program) {
|
||||
mProgram = program;
|
||||
|
||||
@@ -135,31 +124,9 @@ class ImageGLWallpaper {
|
||||
}
|
||||
|
||||
private void setupUniforms() {
|
||||
mUniAod2Opacity = mProgram.getUniformHandle(U_AOD2OPACITY);
|
||||
mUniPer85 = mProgram.getUniformHandle(U_PER85);
|
||||
mUniReveal = mProgram.getUniformHandle(U_REVEAL);
|
||||
mUniTexture = mProgram.getUniformHandle(U_TEXTURE);
|
||||
}
|
||||
|
||||
int getHandle(String name) {
|
||||
switch (name) {
|
||||
case A_POSITION:
|
||||
return mAttrPosition;
|
||||
case A_TEXTURE_COORDINATES:
|
||||
return mAttrTextureCoordinates;
|
||||
case U_AOD2OPACITY:
|
||||
return mUniAod2Opacity;
|
||||
case U_PER85:
|
||||
return mUniPer85;
|
||||
case U_REVEAL:
|
||||
return mUniReveal;
|
||||
case U_TEXTURE:
|
||||
return mUniTexture;
|
||||
default:
|
||||
return HANDLE_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
void draw() {
|
||||
glDrawArrays(GL_TRIANGLES, 0, VERTICES.length / 2);
|
||||
}
|
||||
@@ -200,87 +167,6 @@ class ImageGLWallpaper {
|
||||
glUniform1i(mUniTexture, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method adjust s(x-axis), t(y-axis) texture coordinates to get current display area
|
||||
* of texture and will be used during transition.
|
||||
* The adjustment happens if either the width or height of the surface is larger than
|
||||
* corresponding size of the display area.
|
||||
* If both width and height are larger than corresponding size of the display area,
|
||||
* the adjustment will happen at both s, t side.
|
||||
*
|
||||
* @param surface The size of the surface.
|
||||
* @param scissor The display area.
|
||||
* @param xOffset The offset amount along s axis.
|
||||
* @param yOffset The offset amount along t axis.
|
||||
*/
|
||||
void adjustTextureCoordinates(Rect surface, Rect scissor, float xOffset, float yOffset) {
|
||||
mCurrentTexCoordinate = TEXTURES.clone();
|
||||
|
||||
if (surface == null || scissor == null) {
|
||||
mTextureBuffer.put(mCurrentTexCoordinate);
|
||||
mTextureBuffer.position(0);
|
||||
return;
|
||||
}
|
||||
|
||||
int surfaceWidth = surface.width();
|
||||
int surfaceHeight = surface.height();
|
||||
int scissorWidth = scissor.width();
|
||||
int scissorHeight = scissor.height();
|
||||
|
||||
if (surfaceWidth > scissorWidth) {
|
||||
// Calculate the new s pos in pixels.
|
||||
float pixelS = (float) Math.round((surfaceWidth - scissorWidth) * xOffset);
|
||||
// Calculate the s pos in texture coordinate.
|
||||
float coordinateS = pixelS / surfaceWidth;
|
||||
// Calculate the percentage occupied by the scissor width in surface width.
|
||||
float surfacePercentageW = (float) scissorWidth / surfaceWidth;
|
||||
// Need also consider the case if surface height is smaller than scissor height.
|
||||
if (surfaceHeight < scissorHeight) {
|
||||
// We will narrow the surface percentage to keep aspect ratio.
|
||||
surfacePercentageW *= (float) surfaceHeight / scissorHeight;
|
||||
}
|
||||
// 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 < mCurrentTexCoordinate.length; i += 2) {
|
||||
// indices 2, 4 and 6 are the end of s coordinates.
|
||||
if (i == 2 || i == 4 || i == 6) {
|
||||
mCurrentTexCoordinate[i] = Math.min(1f, s + surfacePercentageW);
|
||||
} else {
|
||||
mCurrentTexCoordinate[i] = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (surfaceHeight > scissorHeight) {
|
||||
// Calculate the new t pos in pixels.
|
||||
float pixelT = (float) Math.round((surfaceHeight - scissorHeight) * yOffset);
|
||||
// Calculate the t pos in texture coordinate.
|
||||
float coordinateT = pixelT / surfaceHeight;
|
||||
// Calculate the percentage occupied by the scissor height in surface height.
|
||||
float surfacePercentageH = (float) scissorHeight / surfaceHeight;
|
||||
// Need also consider the case if surface width is smaller than scissor width.
|
||||
if (surfaceWidth < scissorWidth) {
|
||||
// We will narrow the surface percentage to keep aspect ratio.
|
||||
surfacePercentageH *= (float) surfaceWidth / scissorWidth;
|
||||
}
|
||||
// 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 < mCurrentTexCoordinate.length; i += 2) {
|
||||
// indices 1, 3 and 11 are the end of t coordinates.
|
||||
if (i == 1 || i == 3 || i == 11) {
|
||||
mCurrentTexCoordinate[i] = Math.min(1f, t + surfacePercentageH);
|
||||
} else {
|
||||
mCurrentTexCoordinate[i] = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mTextureBuffer.put(mCurrentTexCoordinate);
|
||||
mTextureBuffer.position(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to dump current state.
|
||||
* @param prefix prefix.
|
||||
@@ -289,17 +175,5 @@ class ImageGLWallpaper {
|
||||
* @param args args.
|
||||
*/
|
||||
public void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('{');
|
||||
if (mCurrentTexCoordinate != null) {
|
||||
for (int i = 0; i < mCurrentTexCoordinate.length; i++) {
|
||||
sb.append(mCurrentTexCoordinate[i]).append(',');
|
||||
if (i == mCurrentTexCoordinate.length - 1) {
|
||||
sb.deleteCharAt(sb.length() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.append('}');
|
||||
out.print(prefix); out.print("mTexCoordinates="); out.println(sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,246 +0,0 @@
|
||||
/*
|
||||
* 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 com.android.systemui.glwallpaper.ImageWallpaperRenderer.WallpaperTexture;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorMatrix;
|
||||
import android.graphics.ColorMatrixColorFilter;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Handler;
|
||||
import android.os.Handler.Callback;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A helper class that computes threshold from a bitmap.
|
||||
* Threshold will be computed each time the user picks a new image wallpaper.
|
||||
*/
|
||||
class ImageProcessHelper {
|
||||
private static final String TAG = ImageProcessHelper.class.getSimpleName();
|
||||
private static final float DEFAULT_THRESHOLD = 0.8f;
|
||||
private static final float DEFAULT_OTSU_THRESHOLD = 0f;
|
||||
private static final float MAX_THRESHOLD = 0.89f;
|
||||
private static final int MSG_UPDATE_THRESHOLD = 1;
|
||||
|
||||
/**
|
||||
* This color matrix will be applied to each pixel to get luminance from rgb by below formula:
|
||||
* Luminance = .2126f * r + .7152f * g + .0722f * b.
|
||||
*/
|
||||
private static final float[] LUMINOSITY_MATRIX = new float[] {
|
||||
.2126f, .0000f, .0000f, .0000f, .0000f,
|
||||
.0000f, .7152f, .0000f, .0000f, .0000f,
|
||||
.0000f, .0000f, .0722f, .0000f, .0000f,
|
||||
.0000f, .0000f, .0000f, 1.000f, .0000f
|
||||
};
|
||||
|
||||
private final Handler mHandler = new Handler(new Callback() {
|
||||
@Override
|
||||
public boolean handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_UPDATE_THRESHOLD:
|
||||
mThreshold = (float) msg.obj;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
private float mThreshold = DEFAULT_THRESHOLD;
|
||||
|
||||
void start(WallpaperTexture texture) {
|
||||
new ThresholdComputeTask(mHandler).execute(texture);
|
||||
}
|
||||
|
||||
float getThreshold() {
|
||||
return Math.min(mThreshold, MAX_THRESHOLD);
|
||||
}
|
||||
|
||||
private static class ThresholdComputeTask extends AsyncTask<WallpaperTexture, Void, Float> {
|
||||
private Handler mUpdateHandler;
|
||||
|
||||
ThresholdComputeTask(Handler handler) {
|
||||
super(handler);
|
||||
mUpdateHandler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Float doInBackground(WallpaperTexture... textures) {
|
||||
WallpaperTexture texture = textures[0];
|
||||
final float[] threshold = new float[] {DEFAULT_THRESHOLD};
|
||||
if (texture == null) {
|
||||
Log.e(TAG, "ThresholdComputeTask: WallpaperTexture not initialized");
|
||||
return threshold[0];
|
||||
}
|
||||
|
||||
texture.use(bitmap -> {
|
||||
if (bitmap != null) {
|
||||
threshold[0] = new Threshold().compute(bitmap);
|
||||
} else {
|
||||
Log.e(TAG, "ThresholdComputeTask: Can't get bitmap");
|
||||
}
|
||||
});
|
||||
return threshold[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Float result) {
|
||||
Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_THRESHOLD, result);
|
||||
mUpdateHandler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Threshold {
|
||||
public float compute(Bitmap bitmap) {
|
||||
Bitmap grayscale = toGrayscale(bitmap);
|
||||
int[] histogram = getHistogram(grayscale);
|
||||
boolean isSolidColor = isSolidColor(grayscale, histogram);
|
||||
|
||||
// We will see gray wallpaper during the transition if solid color wallpaper is set,
|
||||
// please refer to b/130360362#comment16.
|
||||
// As a result, we use Percentile85 rather than Otsus if a solid color wallpaper is set.
|
||||
ThresholdAlgorithm algorithm = isSolidColor ? new Percentile85() : new Otsus();
|
||||
return algorithm.compute(grayscale, histogram);
|
||||
}
|
||||
|
||||
private Bitmap toGrayscale(Bitmap bitmap) {
|
||||
int width = bitmap.getWidth();
|
||||
int height = bitmap.getHeight();
|
||||
|
||||
Bitmap grayscale = Bitmap.createBitmap(width, height, bitmap.getConfig(),
|
||||
false /* hasAlpha */, bitmap.getColorSpace());
|
||||
Canvas canvas = new Canvas(grayscale);
|
||||
ColorMatrix cm = new ColorMatrix(LUMINOSITY_MATRIX);
|
||||
Paint paint = new Paint();
|
||||
paint.setColorFilter(new ColorMatrixColorFilter(cm));
|
||||
canvas.drawBitmap(bitmap, new Matrix(), paint);
|
||||
|
||||
return grayscale;
|
||||
}
|
||||
|
||||
private int[] getHistogram(Bitmap grayscale) {
|
||||
int width = grayscale.getWidth();
|
||||
int height = grayscale.getHeight();
|
||||
|
||||
// TODO: Fine tune the performance here, tracking on b/123615079.
|
||||
int[] histogram = new int[256];
|
||||
for (int row = 0; row < height; row++) {
|
||||
for (int col = 0; col < width; col++) {
|
||||
int pixel = grayscale.getPixel(col, row);
|
||||
int y = Color.red(pixel) + Color.green(pixel) + Color.blue(pixel);
|
||||
histogram[y]++;
|
||||
}
|
||||
}
|
||||
|
||||
return histogram;
|
||||
}
|
||||
|
||||
private boolean isSolidColor(Bitmap bitmap, int[] histogram) {
|
||||
boolean solidColor = false;
|
||||
int pixels = bitmap.getWidth() * bitmap.getHeight();
|
||||
|
||||
// In solid color case, only one element of histogram has value,
|
||||
// which is pixel counts and the value of other elements should be 0.
|
||||
for (int value : histogram) {
|
||||
if (value != 0 && value != pixels) {
|
||||
break;
|
||||
}
|
||||
if (value == pixels) {
|
||||
solidColor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return solidColor;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Percentile85 implements ThresholdAlgorithm {
|
||||
@Override
|
||||
public float compute(Bitmap bitmap, int[] histogram) {
|
||||
float per85 = DEFAULT_THRESHOLD;
|
||||
int pixelCount = bitmap.getWidth() * bitmap.getHeight();
|
||||
float[] acc = new float[256];
|
||||
for (int i = 0; i < acc.length; i++) {
|
||||
acc[i] = (float) histogram[i] / pixelCount;
|
||||
float prev = i == 0 ? 0f : acc[i - 1];
|
||||
float next = acc[i];
|
||||
float idx = (float) (i + 1) / 255;
|
||||
float sum = prev + next;
|
||||
if (prev < 0.85f && sum >= 0.85f) {
|
||||
per85 = idx;
|
||||
}
|
||||
if (i > 0) {
|
||||
acc[i] += acc[i - 1];
|
||||
}
|
||||
}
|
||||
return per85;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Otsus implements ThresholdAlgorithm {
|
||||
@Override
|
||||
public float compute(Bitmap bitmap, int[] histogram) {
|
||||
float threshold = DEFAULT_OTSU_THRESHOLD;
|
||||
float maxVariance = 0;
|
||||
float pixelCount = bitmap.getWidth() * bitmap.getHeight();
|
||||
float[] w = new float[2];
|
||||
float[] m = new float[2];
|
||||
float[] u = new float[2];
|
||||
|
||||
for (int i = 0; i < histogram.length; i++) {
|
||||
m[1] += i * histogram[i];
|
||||
}
|
||||
|
||||
w[1] = pixelCount;
|
||||
for (int tonalValue = 0; tonalValue < histogram.length; tonalValue++) {
|
||||
float dU;
|
||||
float variance;
|
||||
float numPixels = histogram[tonalValue];
|
||||
float tmp = numPixels * tonalValue;
|
||||
w[0] += numPixels;
|
||||
w[1] -= numPixels;
|
||||
|
||||
if (w[0] == 0 || w[1] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
m[0] += tmp;
|
||||
m[1] -= tmp;
|
||||
u[0] = m[0] / w[0];
|
||||
u[1] = m[1] / w[1];
|
||||
dU = u[0] - u[1];
|
||||
variance = w[0] * w[1] * dU * dU;
|
||||
|
||||
if (variance > maxVariance) {
|
||||
threshold = (tonalValue + 1f) / histogram.length;
|
||||
maxVariance = variance;
|
||||
}
|
||||
}
|
||||
return threshold;
|
||||
}
|
||||
}
|
||||
|
||||
private interface ThresholdAlgorithm {
|
||||
float compute(Bitmap bitmap, int[] histogram);
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* 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.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.systemui.Interpolators;
|
||||
|
||||
/**
|
||||
* Use ValueAnimator and appropriate interpolator to control the progress of reveal transition.
|
||||
* The transition will happen while getting awake and quit events.
|
||||
*/
|
||||
class ImageRevealHelper {
|
||||
private static final String TAG = ImageRevealHelper.class.getSimpleName();
|
||||
private static final float MAX_REVEAL = 0f;
|
||||
private static final float MIN_REVEAL = 1f;
|
||||
private static final boolean DEBUG = true;
|
||||
|
||||
private final ValueAnimator mAnimator;
|
||||
private final RevealStateListener mRevealListener;
|
||||
private float mReveal = MAX_REVEAL;
|
||||
private boolean mAwake = false;
|
||||
|
||||
ImageRevealHelper(RevealStateListener listener) {
|
||||
mRevealListener = listener;
|
||||
mAnimator = ValueAnimator.ofFloat();
|
||||
mAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
|
||||
mAnimator.addUpdateListener(animator -> {
|
||||
mReveal = (float) animator.getAnimatedValue();
|
||||
if (mRevealListener != null) {
|
||||
mRevealListener.onRevealStateChanged();
|
||||
}
|
||||
});
|
||||
mAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
private boolean mIsCanceled;
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
mIsCanceled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (!mIsCanceled && mRevealListener != null) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "transition end");
|
||||
}
|
||||
mRevealListener.onRevealEnd();
|
||||
}
|
||||
mIsCanceled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
if (mRevealListener != null) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "transition start");
|
||||
}
|
||||
mRevealListener.onRevealStart(true /* animate */);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public float getReveal() {
|
||||
return mReveal;
|
||||
}
|
||||
|
||||
void updateAwake(boolean awake, long duration) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "updateAwake: awake=" + awake + ", duration=" + duration);
|
||||
}
|
||||
mAnimator.cancel();
|
||||
mAwake = awake;
|
||||
if (duration == 0) {
|
||||
// We are transiting from home to aod or aod to home directly,
|
||||
// we don't need to do transition in these cases.
|
||||
mReveal = mAwake ? MAX_REVEAL : MIN_REVEAL;
|
||||
mRevealListener.onRevealStart(false /* animate */);
|
||||
mRevealListener.onRevealStateChanged();
|
||||
mRevealListener.onRevealEnd();
|
||||
} else {
|
||||
mAnimator.setDuration(duration);
|
||||
mAnimator.setFloatValues(mReveal, mAwake ? MAX_REVEAL : MIN_REVEAL);
|
||||
mAnimator.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener to trace value changes of reveal.
|
||||
*/
|
||||
public interface RevealStateListener {
|
||||
|
||||
/**
|
||||
* Called back while reveal status changes.
|
||||
*/
|
||||
void onRevealStateChanged();
|
||||
|
||||
/**
|
||||
* Called back while reveal starts.
|
||||
*/
|
||||
void onRevealStart(boolean animate);
|
||||
|
||||
/**
|
||||
* Called back while reveal ends.
|
||||
*/
|
||||
void onRevealEnd();
|
||||
}
|
||||
}
|
||||
@@ -19,18 +19,14 @@ package com.android.systemui.glwallpaper;
|
||||
import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;
|
||||
import static android.opengl.GLES20.glClear;
|
||||
import static android.opengl.GLES20.glClearColor;
|
||||
import static android.opengl.GLES20.glUniform1f;
|
||||
import static android.opengl.GLES20.glViewport;
|
||||
|
||||
import android.app.WallpaperManager;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Rect;
|
||||
import android.util.Log;
|
||||
import android.util.MathUtils;
|
||||
import android.util.Size;
|
||||
import android.view.DisplayInfo;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
@@ -42,57 +38,24 @@ import java.util.function.Consumer;
|
||||
/**
|
||||
* A GL renderer for image wallpaper.
|
||||
*/
|
||||
public class ImageWallpaperRenderer implements GLWallpaperRenderer,
|
||||
ImageRevealHelper.RevealStateListener {
|
||||
public class ImageWallpaperRenderer implements GLWallpaperRenderer {
|
||||
private static final String TAG = ImageWallpaperRenderer.class.getSimpleName();
|
||||
private static final float SCALE_VIEWPORT_MIN = 1f;
|
||||
private static final float SCALE_VIEWPORT_MAX = 1.1f;
|
||||
private static final boolean DEBUG = true;
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private final ImageGLProgram mProgram;
|
||||
private final ImageGLWallpaper mWallpaper;
|
||||
private final ImageProcessHelper mImageProcessHelper;
|
||||
private final ImageRevealHelper mImageRevealHelper;
|
||||
|
||||
private SurfaceProxy mProxy;
|
||||
private final Rect mScissor;
|
||||
private final Rect mSurfaceSize = new Rect();
|
||||
private final Rect mViewport = new Rect();
|
||||
private boolean mScissorMode;
|
||||
private float mXOffset;
|
||||
private float mYOffset;
|
||||
private final WallpaperTexture mTexture;
|
||||
|
||||
public ImageWallpaperRenderer(Context context, SurfaceProxy proxy) {
|
||||
public ImageWallpaperRenderer(Context context) {
|
||||
final WallpaperManager wpm = context.getSystemService(WallpaperManager.class);
|
||||
if (wpm == null) {
|
||||
Log.w(TAG, "WallpaperManager not available");
|
||||
}
|
||||
|
||||
mTexture = new WallpaperTexture(wpm);
|
||||
DisplayInfo displayInfo = new DisplayInfo();
|
||||
context.getDisplay().getDisplayInfo(displayInfo);
|
||||
|
||||
// We only do transition in portrait currently, b/137962047.
|
||||
int orientation = context.getResources().getConfiguration().orientation;
|
||||
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
mScissor = new Rect(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
|
||||
} else {
|
||||
mScissor = new Rect(0, 0, displayInfo.logicalHeight, displayInfo.logicalWidth);
|
||||
}
|
||||
|
||||
mProxy = proxy;
|
||||
mProgram = new ImageGLProgram(context);
|
||||
mWallpaper = new ImageGLWallpaper(mProgram);
|
||||
mImageProcessHelper = new ImageProcessHelper();
|
||||
mImageRevealHelper = new ImageRevealHelper(this);
|
||||
|
||||
startProcessingImage();
|
||||
}
|
||||
|
||||
protected void startProcessingImage() {
|
||||
// Compute threshold of the image, this is an async work.
|
||||
mImageProcessHelper.start(mTexture);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -121,103 +84,26 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer,
|
||||
|
||||
@Override
|
||||
public void onDrawFrame() {
|
||||
float threshold = mImageProcessHelper.getThreshold();
|
||||
float reveal = mImageRevealHelper.getReveal();
|
||||
|
||||
glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_AOD2OPACITY), 1);
|
||||
glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_PER85), threshold);
|
||||
glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_REVEAL), reveal);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
// We only need to scale viewport while doing transition.
|
||||
if (mScissorMode) {
|
||||
scaleViewport(reveal);
|
||||
} else {
|
||||
glViewport(0, 0, mSurfaceSize.width(), mSurfaceSize.height());
|
||||
}
|
||||
glViewport(0, 0, mSurfaceSize.width(), mSurfaceSize.height());
|
||||
mWallpaper.useTexture();
|
||||
mWallpaper.draw();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAmbientMode(boolean inAmbientMode, long duration) {
|
||||
mImageRevealHelper.updateAwake(!inAmbientMode, duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateOffsets(float xOffset, float yOffset) {
|
||||
mXOffset = xOffset;
|
||||
mYOffset = yOffset;
|
||||
int left = (int) ((mSurfaceSize.width() - mScissor.width()) * xOffset);
|
||||
int right = left + mScissor.width();
|
||||
mScissor.set(left, mScissor.top, right, mScissor.bottom);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Size reportSurfaceSize() {
|
||||
mTexture.use(null);
|
||||
mTexture.use(null /* consumer */);
|
||||
mSurfaceSize.set(mTexture.getTextureDimensions());
|
||||
return new Size(mSurfaceSize.width(), mSurfaceSize.height());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
mProxy = null;
|
||||
}
|
||||
|
||||
private void scaleViewport(float reveal) {
|
||||
int left = mScissor.left;
|
||||
int top = mScissor.top;
|
||||
int width = mScissor.width();
|
||||
int height = mScissor.height();
|
||||
// Interpolation between SCALE_VIEWPORT_MAX and SCALE_VIEWPORT_MIN by reveal.
|
||||
float vpScaled = MathUtils.lerp(SCALE_VIEWPORT_MIN, SCALE_VIEWPORT_MAX, reveal);
|
||||
// Calculate the offset amount from the lower left corner.
|
||||
float offset = (SCALE_VIEWPORT_MIN - vpScaled) / 2;
|
||||
// Change the viewport.
|
||||
mViewport.set((int) (left + width * offset), (int) (top + height * offset),
|
||||
(int) (width * vpScaled), (int) (height * vpScaled));
|
||||
glViewport(mViewport.left, mViewport.top, mViewport.right, mViewport.bottom);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRevealStateChanged() {
|
||||
mProxy.requestRender();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRevealStart(boolean animate) {
|
||||
if (animate) {
|
||||
mScissorMode = true;
|
||||
// Use current display area of texture.
|
||||
mWallpaper.adjustTextureCoordinates(mSurfaceSize, mScissor, mXOffset, mYOffset);
|
||||
}
|
||||
mProxy.preRender();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRevealEnd() {
|
||||
if (mScissorMode) {
|
||||
mScissorMode = false;
|
||||
// reset texture coordinates to use full texture.
|
||||
mWallpaper.adjustTextureCoordinates(null, null, 0, 0);
|
||||
// We need draw full texture back before finishing render.
|
||||
mProxy.requestRender();
|
||||
}
|
||||
mProxy.postRender();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
|
||||
out.print(prefix); out.print("mProxy="); out.print(mProxy);
|
||||
out.print(prefix); out.print("mSurfaceSize="); out.print(mSurfaceSize);
|
||||
out.print(prefix); out.print("mScissor="); out.print(mScissor);
|
||||
out.print(prefix); out.print("mViewport="); out.print(mViewport);
|
||||
out.print(prefix); out.print("mScissorMode="); out.print(mScissorMode);
|
||||
out.print(prefix); out.print("mXOffset="); out.print(mXOffset);
|
||||
out.print(prefix); out.print("mYOffset="); out.print(mYOffset);
|
||||
out.print(prefix); out.print("threshold="); out.print(mImageProcessHelper.getThreshold());
|
||||
out.print(prefix); out.print("mReveal="); out.print(mImageRevealHelper.getReveal());
|
||||
out.print(prefix); out.print("mWcgContent="); out.print(isWcgContent());
|
||||
mWallpaper.dump(prefix, fd, out, args);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.android.systemui;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
@@ -32,19 +31,16 @@ import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.ColorSpace;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.display.DisplayManagerGlobal;
|
||||
import android.os.Handler;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.util.Size;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.SurfaceHolder;
|
||||
|
||||
import com.android.systemui.glwallpaper.ImageWallpaperRenderer;
|
||||
import com.android.systemui.statusbar.phone.DozeParameters;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -72,8 +68,6 @@ public class ImageWallpaperTest extends SysuiTestCase {
|
||||
@Mock
|
||||
private Bitmap mWallpaperBitmap;
|
||||
@Mock
|
||||
private DozeParameters mDozeParam;
|
||||
@Mock
|
||||
private Handler mHandler;
|
||||
|
||||
private CountDownLatch mEventCountdown;
|
||||
@@ -100,14 +94,13 @@ public class ImageWallpaperTest extends SysuiTestCase {
|
||||
when(wallpaperManager.getBitmap(false)).thenReturn(mWallpaperBitmap);
|
||||
when(mWallpaperBitmap.getColorSpace()).thenReturn(ColorSpace.get(ColorSpace.Named.SRGB));
|
||||
when(mWallpaperBitmap.getConfig()).thenReturn(Bitmap.Config.ARGB_8888);
|
||||
when(mDozeParam.getDisplayNeedsBlanking()).thenReturn(false);
|
||||
}
|
||||
|
||||
private ImageWallpaper createImageWallpaper() {
|
||||
return new ImageWallpaper(mDozeParam) {
|
||||
return new ImageWallpaper() {
|
||||
@Override
|
||||
public Engine onCreateEngine() {
|
||||
return new GLEngine(mDozeParam, mHandler) {
|
||||
return new GLEngine(mHandler) {
|
||||
@Override
|
||||
public Context getDisplayContext() {
|
||||
return mMockContext;
|
||||
@@ -130,75 +123,52 @@ public class ImageWallpaperTest extends SysuiTestCase {
|
||||
};
|
||||
}
|
||||
|
||||
private ImageWallpaperRenderer createImageWallpaperRenderer(ImageWallpaper.GLEngine engine) {
|
||||
return new ImageWallpaperRenderer(mMockContext, engine) {
|
||||
@Override
|
||||
public void startProcessingImage() {
|
||||
// No - Op
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBitmapWallpaper_normal() {
|
||||
// Will use a image wallpaper with dimensions DISPLAY_WIDTH x DISPLAY_WIDTH.
|
||||
// Then we expect the surface size will be also DISPLAY_WIDTH x DISPLAY_WIDTH.
|
||||
// Finally, we assert the transition will not be stopped.
|
||||
verifySurfaceSizeAndAssertTransition(DISPLAY_WIDTH /* bmpWidth */,
|
||||
verifySurfaceSize(DISPLAY_WIDTH /* bmpWidth */,
|
||||
DISPLAY_WIDTH /* bmpHeight */,
|
||||
DISPLAY_WIDTH /* surfaceWidth */,
|
||||
DISPLAY_WIDTH /* surfaceHeight */,
|
||||
false /* assertion */);
|
||||
DISPLAY_WIDTH /* surfaceHeight */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBitmapWallpaper_low_resolution() {
|
||||
// Will use a image wallpaper with dimensions BMP_WIDTH x BMP_HEIGHT.
|
||||
// Then we expect the surface size will be also BMP_WIDTH x BMP_HEIGHT.
|
||||
// Finally, we assert the transition will be stopped.
|
||||
verifySurfaceSizeAndAssertTransition(LOW_BMP_WIDTH /* bmpWidth */,
|
||||
verifySurfaceSize(LOW_BMP_WIDTH /* bmpWidth */,
|
||||
LOW_BMP_HEIGHT /* bmpHeight */,
|
||||
LOW_BMP_WIDTH /* surfaceWidth */,
|
||||
LOW_BMP_HEIGHT /* surfaceHeight */,
|
||||
false /* assertion */);
|
||||
LOW_BMP_HEIGHT /* surfaceHeight */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBitmapWallpaper_too_small() {
|
||||
// Will use a image wallpaper with dimensions INVALID_BMP_WIDTH x INVALID_BMP_HEIGHT.
|
||||
// Then we expect the surface size will be also MIN_SURFACE_WIDTH x MIN_SURFACE_HEIGHT.
|
||||
// Finally, we assert the transition will be stopped.
|
||||
verifySurfaceSizeAndAssertTransition(INVALID_BMP_WIDTH /* bmpWidth */,
|
||||
verifySurfaceSize(INVALID_BMP_WIDTH /* bmpWidth */,
|
||||
INVALID_BMP_HEIGHT /* bmpHeight */,
|
||||
ImageWallpaper.GLEngine.MIN_SURFACE_WIDTH /* surfaceWidth */,
|
||||
ImageWallpaper.GLEngine.MIN_SURFACE_HEIGHT /* surfaceHeight */,
|
||||
false /* assertion */);
|
||||
ImageWallpaper.GLEngine.MIN_SURFACE_HEIGHT /* surfaceHeight */);
|
||||
}
|
||||
|
||||
private void verifySurfaceSizeAndAssertTransition(int bmpWidth, int bmpHeight,
|
||||
int surfaceWidth, int surfaceHeight, boolean assertion) {
|
||||
private void verifySurfaceSize(int bmpWidth, int bmpHeight,
|
||||
int surfaceWidth, int surfaceHeight) {
|
||||
ImageWallpaper.GLEngine wallpaperEngine =
|
||||
(ImageWallpaper.GLEngine) createImageWallpaper().onCreateEngine();
|
||||
|
||||
ImageWallpaper.GLEngine engineSpy = spy(wallpaperEngine);
|
||||
when(engineSpy.mIsHighEndGfx).thenReturn(true);
|
||||
|
||||
when(mWallpaperBitmap.getWidth()).thenReturn(bmpWidth);
|
||||
when(mWallpaperBitmap.getHeight()).thenReturn(bmpHeight);
|
||||
|
||||
ImageWallpaperRenderer renderer = createImageWallpaperRenderer(engineSpy);
|
||||
ImageWallpaperRenderer renderer = new ImageWallpaperRenderer(mMockContext);
|
||||
doReturn(renderer).when(engineSpy).getRendererInstance();
|
||||
engineSpy.onCreate(engineSpy.getSurfaceHolder());
|
||||
|
||||
verify(mSurfaceHolder, times(1)).setFixedSize(surfaceWidth, surfaceHeight);
|
||||
assertWithMessage("setFixedSizeAllowed should have been called.").that(
|
||||
mEventCountdown.getCount()).isEqualTo(0);
|
||||
|
||||
Size frameSize = renderer.reportSurfaceSize();
|
||||
Rect frame = new Rect(0, 0, frameSize.getWidth(), frameSize.getHeight());
|
||||
when(mSurfaceHolder.getSurfaceFrame()).thenReturn(frame);
|
||||
|
||||
assertThat(engineSpy.checkIfShouldStopTransition()).isEqualTo(assertion);
|
||||
// destroy
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
@@ -84,22 +85,17 @@ public class EglHelperTest extends SysuiTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInit_finish() {
|
||||
public void testInit_normal() {
|
||||
mEglHelper.init(mSurfaceHolder, false /* wideColorGamut */);
|
||||
assertThat(mEglHelper.hasEglDisplay()).isTrue();
|
||||
assertThat(mEglHelper.hasEglContext()).isTrue();
|
||||
assertThat(mEglHelper.hasEglSurface()).isTrue();
|
||||
verify(mEglHelper).askCreatingEglWindowSurface(
|
||||
any(SurfaceHolder.class), eq(null), anyInt());
|
||||
|
||||
mEglHelper.finish();
|
||||
assertThat(mEglHelper.hasEglSurface()).isFalse();
|
||||
assertThat(mEglHelper.hasEglContext()).isFalse();
|
||||
assertThat(mEglHelper.hasEglDisplay()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInit_finish_wide_gamut() {
|
||||
public void testInit_wide_gamut() {
|
||||
// In EglHelper, EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT = 0x3490;
|
||||
doReturn(0x3490).when(mEglHelper).getWcgCapability();
|
||||
// In EglHelper, KHR_GL_COLOR_SPACE = "EGL_KHR_gl_colorspace";
|
||||
@@ -113,10 +109,10 @@ public class EglHelperTest extends SysuiTestCase {
|
||||
.askCreatingEglWindowSurface(any(SurfaceHolder.class), ac.capture(), anyInt());
|
||||
assertThat(ac.getValue()).isNotNull();
|
||||
assertThat(ac.getValue()).isEqualTo(expectedArgument);
|
||||
mEglHelper.finish();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testFinish_shouldNotCrash() {
|
||||
mEglHelper.terminateEglDisplay();
|
||||
assertThat(mEglHelper.hasEglDisplay()).isFalse();
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* 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 com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper
|
||||
public class ImageRevealHelperTest extends SysuiTestCase {
|
||||
|
||||
static final int ANIMATION_DURATION = 500;
|
||||
ImageRevealHelper mImageRevealHelper;
|
||||
ImageRevealHelper.RevealStateListener mRevealStateListener;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mRevealStateListener = new ImageRevealHelper.RevealStateListener() {
|
||||
@Override
|
||||
public void onRevealStateChanged() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRevealStart(boolean animate) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRevealEnd() {
|
||||
// no-op
|
||||
}
|
||||
};
|
||||
mImageRevealHelper = new ImageRevealHelper(mRevealStateListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBiometricAuthUnlockAnimateImageRevealState_shouldNotBlackoutScreen() {
|
||||
assertThat(mImageRevealHelper.getReveal()).isEqualTo(0f);
|
||||
|
||||
mImageRevealHelper.updateAwake(true /* awake */, ANIMATION_DURATION);
|
||||
assertThat(mImageRevealHelper.getReveal()).isEqualTo(0f);
|
||||
|
||||
// When device unlock through Biometric, should not show reveal transition
|
||||
mImageRevealHelper.updateAwake(false /* awake */, 0);
|
||||
assertThat(mImageRevealHelper.getReveal()).isEqualTo(1f);
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.android.systemui.glwallpaper;
|
||||
|
||||
import static com.android.systemui.glwallpaper.GLWallpaperRenderer.SurfaceProxy;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
@@ -48,30 +46,12 @@ import java.util.Set;
|
||||
public class ImageWallpaperRendererTest extends SysuiTestCase {
|
||||
|
||||
private WallpaperManager mWpmSpy;
|
||||
private SurfaceProxy mSurfaceProxy;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
final WallpaperManager wpm = mContext.getSystemService(WallpaperManager.class);
|
||||
mWpmSpy = spy(wpm);
|
||||
mContext.addMockSystemService(WallpaperManager.class, mWpmSpy);
|
||||
|
||||
mSurfaceProxy = new SurfaceProxy() {
|
||||
@Override
|
||||
public void requestRender() {
|
||||
// NO-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRender() {
|
||||
// No-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postRender() {
|
||||
// No-op
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -91,12 +71,12 @@ public class ImageWallpaperRendererTest extends SysuiTestCase {
|
||||
doReturn(supportedWideGamuts).when(cmProxySpy).getSupportedColorSpaces();
|
||||
|
||||
mWpmSpy.setBitmap(p3Bitmap);
|
||||
ImageWallpaperRenderer rendererP3 = new ImageWallpaperRenderer(mContext, mSurfaceProxy);
|
||||
ImageWallpaperRenderer rendererP3 = new ImageWallpaperRenderer(mContext);
|
||||
rendererP3.reportSurfaceSize();
|
||||
assertThat(rendererP3.isWcgContent()).isTrue();
|
||||
|
||||
mWpmSpy.setBitmap(srgbBitmap);
|
||||
ImageWallpaperRenderer renderer = new ImageWallpaperRenderer(mContext, mSurfaceProxy);
|
||||
ImageWallpaperRenderer renderer = new ImageWallpaperRenderer(mContext);
|
||||
assertThat(renderer.isWcgContent()).isFalse();
|
||||
} finally {
|
||||
srgbBitmap.recycle();
|
||||
|
||||
Reference in New Issue
Block a user