Merge "Remove usage of GlEngine" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
5538ee1c74
@@ -23,11 +23,6 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
coreApp="true">
|
||||
|
||||
<!-- Using OpenGL ES 2.0 -->
|
||||
<uses-feature
|
||||
android:glEsVersion="0x00020000"
|
||||
android:required="true" />
|
||||
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
<!-- Used to read wallpaper -->
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
precision mediump float;
|
||||
|
||||
// The actual wallpaper texture.
|
||||
uniform sampler2D uTexture;
|
||||
|
||||
varying vec2 vTextureCoordinates;
|
||||
|
||||
void main() {
|
||||
// gets the pixel value of the wallpaper for this uv coordinates on screen.
|
||||
gl_FragColor = texture2D(uTexture, vTextureCoordinates);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
attribute vec4 aPosition;
|
||||
attribute vec2 aTextureCoordinates;
|
||||
varying vec2 vTextureCoordinates;
|
||||
|
||||
void main() {
|
||||
vTextureCoordinates = aTextureCoordinates;
|
||||
gl_Position = aPosition;
|
||||
}
|
||||
@@ -254,10 +254,6 @@ object Flags {
|
||||
// TODO(b/254512848): Tracking Bug
|
||||
val REGION_SAMPLING = unreleasedFlag(801, "region_sampling", teamfood = true)
|
||||
|
||||
// 802 - wallpaper rendering
|
||||
// TODO(b/254512923): Tracking Bug
|
||||
@JvmField val USE_CANVAS_RENDERER = unreleasedFlag(802, "use_canvas_renderer")
|
||||
|
||||
// 803 - screen contents translation
|
||||
// TODO(b/254513187): Tracking Bug
|
||||
val SCREEN_CONTENTS_TRANSLATION = unreleasedFlag(803, "screen_contents_translation")
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.android.systemui.wallpapers;
|
||||
|
||||
import static com.android.systemui.flags.Flags.USE_CANVAS_RENDERER;
|
||||
|
||||
import android.app.WallpaperColors;
|
||||
import android.app.WallpaperManager;
|
||||
import android.graphics.Bitmap;
|
||||
@@ -27,16 +25,10 @@ import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.hardware.display.DisplayManager.DisplayListener;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.SystemClock;
|
||||
import android.os.Trace;
|
||||
import android.os.UserHandle;
|
||||
import android.service.wallpaper.WallpaperService;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
import android.util.MathUtils;
|
||||
import android.util.Size;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.WindowManager;
|
||||
@@ -45,16 +37,10 @@ import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.systemui.dagger.qualifiers.Background;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor;
|
||||
import com.android.systemui.wallpapers.canvas.WallpaperLocalColorExtractor;
|
||||
import com.android.systemui.wallpapers.gl.EglHelper;
|
||||
import com.android.systemui.wallpapers.gl.ImageWallpaperRenderer;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -64,455 +50,32 @@ import javax.inject.Inject;
|
||||
*/
|
||||
@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;
|
||||
private static final @android.annotation.NonNull RectF LOCAL_COLOR_BOUNDS =
|
||||
new RectF(0, 0, 1, 1);
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private final ArrayList<RectF> mLocalColorsToAdd = new ArrayList<>();
|
||||
private final ArraySet<RectF> mColorAreas = new ArraySet<>();
|
||||
// keep track of the number of pages of the launcher for local color extraction purposes
|
||||
private volatile int mPages = 1;
|
||||
private boolean mPagesComputed = false;
|
||||
private HandlerThread mWorker;
|
||||
// scaled down version
|
||||
private Bitmap mMiniBitmap;
|
||||
private final FeatureFlags mFeatureFlags;
|
||||
|
||||
// used in canvasEngine to load/unload the bitmap and extract the colors
|
||||
// used for most tasks (call canvas.drawBitmap, load/unload the bitmap)
|
||||
@Background
|
||||
private final DelayableExecutor mBackgroundExecutor;
|
||||
|
||||
// wait at least this duration before unloading the bitmap
|
||||
private static final int DELAY_UNLOAD_BITMAP = 2000;
|
||||
|
||||
@Inject
|
||||
public ImageWallpaper(FeatureFlags featureFlags,
|
||||
@Background DelayableExecutor backgroundExecutor) {
|
||||
public ImageWallpaper(@Background DelayableExecutor backgroundExecutor) {
|
||||
super();
|
||||
mFeatureFlags = featureFlags;
|
||||
mBackgroundExecutor = backgroundExecutor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
mWorker = new HandlerThread(TAG);
|
||||
mWorker.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Engine onCreateEngine() {
|
||||
return mFeatureFlags.isEnabled(USE_CANVAS_RENDERER) ? new CanvasEngine() : new GLEngine();
|
||||
return new CanvasEngine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
mWorker.quitSafely();
|
||||
mWorker = null;
|
||||
mMiniBitmap = null;
|
||||
}
|
||||
|
||||
class GLEngine extends Engine implements DisplayListener {
|
||||
// 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 = 128;
|
||||
@VisibleForTesting
|
||||
static final int MIN_SURFACE_HEIGHT = 128;
|
||||
|
||||
private ImageWallpaperRenderer mRenderer;
|
||||
private EglHelper mEglHelper;
|
||||
private final Runnable mFinishRenderingTask = this::finishRendering;
|
||||
private boolean mNeedRedraw;
|
||||
|
||||
private boolean mDisplaySizeValid = false;
|
||||
private int mDisplayWidth = 1;
|
||||
private int mDisplayHeight = 1;
|
||||
|
||||
private int mImgWidth = 1;
|
||||
private int mImgHeight = 1;
|
||||
|
||||
GLEngine() { }
|
||||
|
||||
@VisibleForTesting
|
||||
GLEngine(Handler handler) {
|
||||
super(SystemClock::elapsedRealtime, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SurfaceHolder surfaceHolder) {
|
||||
Trace.beginSection("ImageWallpaper.Engine#onCreate");
|
||||
mEglHelper = getEglHelperInstance();
|
||||
// Deferred init renderer because we need to get wallpaper by display context.
|
||||
mRenderer = getRendererInstance();
|
||||
setFixedSizeAllowed(true);
|
||||
updateSurfaceSize();
|
||||
setShowForAllUsers(true);
|
||||
|
||||
mRenderer.setOnBitmapChanged(b -> {
|
||||
mLocalColorsToAdd.addAll(mColorAreas);
|
||||
if (mLocalColorsToAdd.size() > 0) {
|
||||
updateMiniBitmapAndNotify(b);
|
||||
}
|
||||
});
|
||||
getDisplayContext().getSystemService(DisplayManager.class)
|
||||
.registerDisplayListener(this, mWorker.getThreadHandler());
|
||||
Trace.endSection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayAdded(int displayId) { }
|
||||
|
||||
@Override
|
||||
public void onDisplayRemoved(int displayId) { }
|
||||
|
||||
@Override
|
||||
public void onDisplayChanged(int displayId) {
|
||||
if (displayId == getDisplayContext().getDisplayId()) {
|
||||
mDisplaySizeValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
EglHelper getEglHelperInstance() {
|
||||
return new EglHelper();
|
||||
}
|
||||
|
||||
ImageWallpaperRenderer getRendererInstance() {
|
||||
return new ImageWallpaperRenderer(getDisplayContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOffsetsChanged(float xOffset, float yOffset,
|
||||
float xOffsetStep, float yOffsetStep,
|
||||
int xPixelOffset, int yPixelOffset) {
|
||||
final int pages;
|
||||
if (xOffsetStep > 0 && xOffsetStep <= 1) {
|
||||
pages = (int) Math.round(1 / xOffsetStep) + 1;
|
||||
} else {
|
||||
pages = 1;
|
||||
}
|
||||
if (pages == mPages) return;
|
||||
mPages = pages;
|
||||
if (mMiniBitmap == null || mMiniBitmap.isRecycled()) return;
|
||||
mWorker.getThreadHandler().post(() ->
|
||||
computeAndNotifyLocalColors(new ArrayList<>(mColorAreas), mMiniBitmap));
|
||||
}
|
||||
|
||||
private void updateMiniBitmapAndNotify(Bitmap b) {
|
||||
if (b == null) return;
|
||||
int size = Math.min(b.getWidth(), b.getHeight());
|
||||
float scale = 1.0f;
|
||||
if (size > MIN_SURFACE_WIDTH) {
|
||||
scale = (float) MIN_SURFACE_WIDTH / (float) size;
|
||||
}
|
||||
mImgHeight = b.getHeight();
|
||||
mImgWidth = b.getWidth();
|
||||
mMiniBitmap = Bitmap.createScaledBitmap(b, (int) Math.max(scale * b.getWidth(), 1),
|
||||
(int) Math.max(scale * b.getHeight(), 1), false);
|
||||
computeAndNotifyLocalColors(mLocalColorsToAdd, mMiniBitmap);
|
||||
mLocalColorsToAdd.clear();
|
||||
}
|
||||
|
||||
private void updateSurfaceSize() {
|
||||
Trace.beginSection("ImageWallpaper#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);
|
||||
Trace.endSection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldZoomOutWallpaper() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldWaitForEngineShown() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
getDisplayContext().getSystemService(DisplayManager.class)
|
||||
.unregisterDisplayListener(this);
|
||||
mMiniBitmap = null;
|
||||
mWorker.getThreadHandler().post(() -> {
|
||||
mRenderer.finish();
|
||||
mRenderer = null;
|
||||
mEglHelper.finish();
|
||||
mEglHelper = null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLocalColorExtraction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLocalColorsAreas(@NonNull List<RectF> regions) {
|
||||
mWorker.getThreadHandler().post(() -> {
|
||||
if (mColorAreas.size() + mLocalColorsToAdd.size() == 0) {
|
||||
setOffsetNotificationsEnabled(true);
|
||||
}
|
||||
Bitmap bitmap = mMiniBitmap;
|
||||
if (bitmap == null) {
|
||||
mLocalColorsToAdd.addAll(regions);
|
||||
if (mRenderer != null) mRenderer.use(this::updateMiniBitmapAndNotify);
|
||||
} else {
|
||||
computeAndNotifyLocalColors(regions, bitmap);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void computeAndNotifyLocalColors(@NonNull List<RectF> regions, Bitmap b) {
|
||||
List<WallpaperColors> colors = getLocalWallpaperColors(regions, b);
|
||||
mColorAreas.addAll(regions);
|
||||
try {
|
||||
notifyLocalColorsChanged(regions, colors);
|
||||
} catch (RuntimeException e) {
|
||||
Log.e(TAG, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLocalColorsAreas(@NonNull List<RectF> regions) {
|
||||
mWorker.getThreadHandler().post(() -> {
|
||||
mColorAreas.removeAll(regions);
|
||||
mLocalColorsToAdd.removeAll(regions);
|
||||
if (mColorAreas.size() + mLocalColorsToAdd.size() == 0) {
|
||||
setOffsetNotificationsEnabled(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the logical coordinates into wallpaper coordinates.
|
||||
*
|
||||
* Logical coordinates are organised such that the various pages are non-overlapping. So,
|
||||
* if there are n pages, the first page will have its X coordinate on the range [0-1/n].
|
||||
*
|
||||
* The real pages are overlapping. If the Wallpaper are a width Ww and the screen a width
|
||||
* Ws, the relative width of a page Wr is Ws/Ww. This does not change if the number of
|
||||
* pages increase.
|
||||
* If there are n pages, the page k starts at the offset k * (1 - Wr) / (n - 1), as the
|
||||
* last page is at position (1-Wr) and the others are regularly spread on the range [0-
|
||||
* (1-Wr)].
|
||||
*/
|
||||
private RectF pageToImgRect(RectF area) {
|
||||
if (!mDisplaySizeValid) {
|
||||
Rect window = getDisplayContext()
|
||||
.getSystemService(WindowManager.class)
|
||||
.getCurrentWindowMetrics()
|
||||
.getBounds();
|
||||
mDisplayWidth = window.width();
|
||||
mDisplayHeight = window.height();
|
||||
mDisplaySizeValid = true;
|
||||
}
|
||||
|
||||
// Width of a page for the caller of this API.
|
||||
float virtualPageWidth = 1f / (float) mPages;
|
||||
float leftPosOnPage = (area.left % virtualPageWidth) / virtualPageWidth;
|
||||
float rightPosOnPage = (area.right % virtualPageWidth) / virtualPageWidth;
|
||||
int currentPage = (int) Math.floor(area.centerX() / virtualPageWidth);
|
||||
|
||||
RectF imgArea = new RectF();
|
||||
|
||||
if (mImgWidth == 0 || mImgHeight == 0 || mDisplayWidth <= 0 || mDisplayHeight <= 0) {
|
||||
return imgArea;
|
||||
}
|
||||
|
||||
imgArea.bottom = area.bottom;
|
||||
imgArea.top = area.top;
|
||||
|
||||
float imageScale = Math.min(((float) mImgHeight) / mDisplayHeight, 1);
|
||||
float mappedScreenWidth = mDisplayWidth * imageScale;
|
||||
float pageWidth = Math.min(1.0f,
|
||||
mImgWidth > 0 ? mappedScreenWidth / (float) mImgWidth : 1.f);
|
||||
float pageOffset = (1 - pageWidth) / (float) (mPages - 1);
|
||||
|
||||
imgArea.left = MathUtils.constrain(
|
||||
leftPosOnPage * pageWidth + currentPage * pageOffset, 0, 1);
|
||||
imgArea.right = MathUtils.constrain(
|
||||
rightPosOnPage * pageWidth + currentPage * pageOffset, 0, 1);
|
||||
if (imgArea.left > imgArea.right) {
|
||||
// take full page
|
||||
imgArea.left = 0;
|
||||
imgArea.right = 1;
|
||||
}
|
||||
return imgArea;
|
||||
}
|
||||
|
||||
private List<WallpaperColors> getLocalWallpaperColors(@NonNull List<RectF> areas,
|
||||
Bitmap b) {
|
||||
List<WallpaperColors> colors = new ArrayList<>(areas.size());
|
||||
for (int i = 0; i < areas.size(); i++) {
|
||||
RectF area = pageToImgRect(areas.get(i));
|
||||
if (area == null || !LOCAL_COLOR_BOUNDS.contains(area)) {
|
||||
colors.add(null);
|
||||
continue;
|
||||
}
|
||||
Rect subImage = new Rect(
|
||||
(int) Math.floor(area.left * b.getWidth()),
|
||||
(int) Math.floor(area.top * b.getHeight()),
|
||||
(int) Math.ceil(area.right * b.getWidth()),
|
||||
(int) Math.ceil(area.bottom * b.getHeight()));
|
||||
if (subImage.isEmpty()) {
|
||||
// Do not notify client. treat it as too small to sample
|
||||
colors.add(null);
|
||||
continue;
|
||||
}
|
||||
Bitmap colorImg = Bitmap.createBitmap(b,
|
||||
subImage.left, subImage.top, subImage.width(), subImage.height());
|
||||
WallpaperColors color = WallpaperColors.fromBitmap(colorImg);
|
||||
colors.add(color);
|
||||
}
|
||||
return colors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceCreated(SurfaceHolder holder) {
|
||||
if (mWorker == null) return;
|
||||
mWorker.getThreadHandler().post(() -> {
|
||||
Trace.beginSection("ImageWallpaper#onSurfaceCreated");
|
||||
mEglHelper.init(holder, needSupportWideColorGamut());
|
||||
mRenderer.onSurfaceCreated();
|
||||
Trace.endSection();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||
if (mWorker == null) return;
|
||||
mWorker.getThreadHandler().post(() -> mRenderer.onSurfaceChanged(width, height));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
|
||||
if (mWorker == null) return;
|
||||
mWorker.getThreadHandler().post(this::drawFrame);
|
||||
}
|
||||
|
||||
private void drawFrame() {
|
||||
Trace.beginSection("ImageWallpaper#drawFrame");
|
||||
preRender();
|
||||
requestRender();
|
||||
postRender();
|
||||
Trace.endSection();
|
||||
}
|
||||
|
||||
public void preRender() {
|
||||
// This method should only be invoked from worker thread.
|
||||
Trace.beginSection("ImageWallpaper#preRender");
|
||||
preRenderInternal();
|
||||
Trace.endSection();
|
||||
}
|
||||
|
||||
private void preRenderInternal() {
|
||||
boolean contextRecreated = false;
|
||||
Rect frame = getSurfaceHolder().getSurfaceFrame();
|
||||
cancelFinishRenderingTask();
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we need to recreate egl surface.
|
||||
if (mEglHelper.hasEglContext() && !mEglHelper.hasEglSurface()) {
|
||||
if (!mEglHelper.createEglSurface(getSurfaceHolder(), needSupportWideColorGamut())) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
public void requestRender() {
|
||||
// This method should only be invoked from worker thread.
|
||||
Trace.beginSection("ImageWallpaper#requestRender");
|
||||
requestRenderInternal();
|
||||
Trace.endSection();
|
||||
}
|
||||
|
||||
private void requestRenderInternal() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public void postRender() {
|
||||
// This method should only be invoked from worker thread.
|
||||
scheduleFinishRendering();
|
||||
reportEngineShown(false /* waitForEngineShown */);
|
||||
}
|
||||
|
||||
private void cancelFinishRenderingTask() {
|
||||
if (mWorker == null) return;
|
||||
mWorker.getThreadHandler().removeCallbacks(mFinishRenderingTask);
|
||||
}
|
||||
|
||||
private void scheduleFinishRendering() {
|
||||
if (mWorker == null) return;
|
||||
cancelFinishRenderingTask();
|
||||
mWorker.getThreadHandler().postDelayed(mFinishRenderingTask, DELAY_FINISH_RENDERING);
|
||||
}
|
||||
|
||||
private void finishRendering() {
|
||||
Trace.beginSection("ImageWallpaper#finishRendering");
|
||||
if (mEglHelper != null) {
|
||||
mEglHelper.destroyEglSurface();
|
||||
mEglHelper.destroyEglContext();
|
||||
}
|
||||
Trace.endSection();
|
||||
}
|
||||
|
||||
private boolean needSupportWideColorGamut() {
|
||||
return mRenderer.isWcgContent();
|
||||
}
|
||||
|
||||
@Override
|
||||
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("valid surface=");
|
||||
out.println(getSurfaceHolder() != null && getSurfaceHolder().getSurface() != null
|
||||
? getSurfaceHolder().getSurface().isValid()
|
||||
: "null");
|
||||
|
||||
out.print(prefix); out.print("surface frame=");
|
||||
out.println(getSurfaceHolder() != null ? getSurfaceHolder().getSurfaceFrame() : "null");
|
||||
|
||||
mEglHelper.dump(prefix, fd, out, args);
|
||||
mRenderer.dump(prefix, fd, out, args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CanvasEngine extends WallpaperService.Engine implements DisplayListener {
|
||||
private WallpaperManager mWallpaperManager;
|
||||
private final WallpaperLocalColorExtractor mWallpaperLocalColorExtractor;
|
||||
@@ -736,13 +299,8 @@ public class ImageWallpaper extends WallpaperService {
|
||||
// be loaded, we will go into a cycle. Don't do a build where the
|
||||
// default wallpaper can't be loaded.
|
||||
Log.w(TAG, "Unable to load wallpaper!", exception);
|
||||
try {
|
||||
mWallpaperManager.clear(WallpaperManager.FLAG_SYSTEM);
|
||||
} catch (IOException ex) {
|
||||
// now we're really screwed.
|
||||
Log.w(TAG, "Unable reset to default wallpaper!", ex);
|
||||
}
|
||||
|
||||
mWallpaperManager.clearWallpaper(
|
||||
WallpaperManager.FLAG_SYSTEM, UserHandle.USER_CURRENT);
|
||||
try {
|
||||
bitmap = mWallpaperManager.getBitmapAsUser(UserHandle.USER_CURRENT, false);
|
||||
} catch (RuntimeException | OutOfMemoryError e) {
|
||||
@@ -868,7 +426,6 @@ public class ImageWallpaper extends WallpaperService {
|
||||
mWallpaperLocalColorExtractor.setDisplayDimensions(window.width(), window.height());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
|
||||
super.dump(prefix, fd, out, args);
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
|
||||
package com.android.systemui.wallpapers.canvas;
|
||||
package com.android.systemui.wallpapers;
|
||||
|
||||
import android.app.WallpaperColors;
|
||||
import android.graphics.Bitmap;
|
||||
@@ -31,7 +31,6 @@ import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.systemui.dagger.qualifiers.Background;
|
||||
import com.android.systemui.util.Assert;
|
||||
import com.android.systemui.wallpapers.ImageWallpaper;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
@@ -1,380 +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.wallpapers.gl;
|
||||
|
||||
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_EXTENSIONS;
|
||||
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.eglQueryString;
|
||||
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.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceHolder;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A helper class to handle EGL management.
|
||||
*/
|
||||
public class EglHelper {
|
||||
private static final String TAG = EglHelper.class.getSimpleName();
|
||||
private static final int OPENGLES_VERSION = 2;
|
||||
// Below two constants make drawing at low priority, so other things can preempt our drawing.
|
||||
private static final int EGL_CONTEXT_PRIORITY_LEVEL_IMG = 0x3100;
|
||||
private static final int EGL_CONTEXT_PRIORITY_LOW_IMG = 0x3103;
|
||||
private static final boolean DEBUG = true;
|
||||
|
||||
private static final int EGL_GL_COLORSPACE_KHR = 0x309D;
|
||||
private static final int EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT = 0x3490;
|
||||
|
||||
private static final String EGL_IMG_CONTEXT_PRIORITY = "EGL_IMG_context_priority";
|
||||
|
||||
/**
|
||||
* https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_gl_colorspace.txt
|
||||
*/
|
||||
private static final String KHR_GL_COLOR_SPACE = "EGL_KHR_gl_colorspace";
|
||||
|
||||
/**
|
||||
* https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_gl_colorspace_display_p3_passthrough.txt
|
||||
*/
|
||||
private static final String EXT_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH =
|
||||
"EGL_EXT_gl_colorspace_display_p3_passthrough";
|
||||
|
||||
private EGLDisplay mEglDisplay;
|
||||
private EGLConfig mEglConfig;
|
||||
private EGLContext mEglContext;
|
||||
private EGLSurface mEglSurface;
|
||||
private final int[] mEglVersion = new int[2];
|
||||
private boolean mEglReady;
|
||||
private final Set<String> mExts;
|
||||
|
||||
public EglHelper() {
|
||||
mExts = new HashSet<>();
|
||||
connectDisplay();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize render context.
|
||||
* @param surfaceHolder surface holder.
|
||||
* @param wideColorGamut claim if a wcg surface is necessary.
|
||||
* @return true if the render context is ready.
|
||||
*/
|
||||
public boolean init(SurfaceHolder surfaceHolder, boolean wideColorGamut) {
|
||||
if (!hasEglDisplay() && !connectDisplay()) {
|
||||
Log.w(TAG, "Can not connect display, abort!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!eglInitialize(mEglDisplay, mEglVersion, 0 /* majorOffset */,
|
||||
mEglVersion, 1 /* minorOffset */)) {
|
||||
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, wideColorGamut)) {
|
||||
Log.w(TAG, "Can't create EGLSurface!");
|
||||
return false;
|
||||
}
|
||||
|
||||
mEglReady = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean connectDisplay() {
|
||||
mExts.clear();
|
||||
mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
if (!hasEglDisplay()) {
|
||||
Log.w(TAG, "eglGetDisplay failed: " + GLUtils.getEGLErrorString(eglGetError()));
|
||||
return false;
|
||||
}
|
||||
String queryString = eglQueryString(mEglDisplay, EGL_EXTENSIONS);
|
||||
if (!TextUtils.isEmpty(queryString)) {
|
||||
Collections.addAll(mExts, queryString.split(" "));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean checkExtensionCapability(String extName) {
|
||||
return mExts.contains(extName);
|
||||
}
|
||||
|
||||
int getWcgCapability() {
|
||||
if (checkExtensionCapability(EXT_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH)) {
|
||||
return EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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, 0,
|
||||
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.
|
||||
* @param wcg if need to support wcg.
|
||||
* @return true if EglSurface is ready.
|
||||
*/
|
||||
public boolean createEglSurface(SurfaceHolder surfaceHolder, boolean wcg) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "createEglSurface start");
|
||||
}
|
||||
|
||||
if (hasEglDisplay() && surfaceHolder.getSurface().isValid()) {
|
||||
int[] attrs = null;
|
||||
int wcgCapability = getWcgCapability();
|
||||
if (wcg && checkExtensionCapability(KHR_GL_COLOR_SPACE) && wcgCapability > 0) {
|
||||
attrs = new int[] {EGL_GL_COLORSPACE_KHR, wcgCapability, EGL_NONE};
|
||||
}
|
||||
mEglSurface = askCreatingEglWindowSurface(surfaceHolder, attrs, 0 /* offset */);
|
||||
} else {
|
||||
Log.w(TAG, "Create EglSurface failed: hasEglDisplay=" + hasEglDisplay()
|
||||
+ ", has valid surface=" + surfaceHolder.getSurface().isValid());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasEglSurface()) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "createEglSurface done");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
EGLSurface askCreatingEglWindowSurface(SurfaceHolder holder, int[] attrs, int offset) {
|
||||
return eglCreateWindowSurface(mEglDisplay, mEglConfig, holder, attrs, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy EglSurface.
|
||||
*/
|
||||
public void destroyEglSurface() {
|
||||
if (hasEglSurface()) {
|
||||
eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
eglDestroySurface(mEglDisplay, mEglSurface);
|
||||
mEglSurface = EGL_NO_SURFACE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "createEglContext start");
|
||||
}
|
||||
|
||||
int[] attrib_list = new int[5];
|
||||
int idx = 0;
|
||||
attrib_list[idx++] = EGL_CONTEXT_CLIENT_VERSION;
|
||||
attrib_list[idx++] = OPENGLES_VERSION;
|
||||
if (checkExtensionCapability(EGL_IMG_CONTEXT_PRIORITY)) {
|
||||
attrib_list[idx++] = EGL_CONTEXT_PRIORITY_LEVEL_IMG;
|
||||
attrib_list[idx++] = EGL_CONTEXT_PRIORITY_LOW_IMG;
|
||||
}
|
||||
attrib_list[idx] = EGL_NONE;
|
||||
if (hasEglDisplay()) {
|
||||
mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attrib_list, 0);
|
||||
} else {
|
||||
Log.w(TAG, "mEglDisplay is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasEglContext()) {
|
||||
Log.w(TAG, "eglCreateContext failed: " + GLUtils.getEGLErrorString(eglGetError()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "createEglContext done");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy EglContext.
|
||||
*/
|
||||
public void destroyEglContext() {
|
||||
if (hasEglContext()) {
|
||||
eglDestroyContext(mEglDisplay, mEglContext);
|
||||
mEglContext = EGL_NO_CONTEXT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we have EglContext.
|
||||
* @return true if EglContext is ready.
|
||||
*/
|
||||
public boolean hasEglContext() {
|
||||
return mEglContext != null && mEglContext != EGL_NO_CONTEXT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we have EglDisplay.
|
||||
* @return true if EglDisplay is ready.
|
||||
*/
|
||||
public boolean hasEglDisplay() {
|
||||
return mEglDisplay != null && mEglDisplay != EGL_NO_DISPLAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
if (hasEglDisplay()) {
|
||||
terminateEglDisplay();
|
||||
}
|
||||
mEglReady = false;
|
||||
}
|
||||
|
||||
void terminateEglDisplay() {
|
||||
eglTerminate(mEglDisplay);
|
||||
mEglDisplay = EGL_NO_DISPLAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to dump current state.
|
||||
* @param prefix prefix.
|
||||
* @param fd fd.
|
||||
* @param out out.
|
||||
* @param args args.
|
||||
*/
|
||||
public void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
|
||||
String eglVersion = mEglVersion[0] + "." + mEglVersion[1];
|
||||
out.print(prefix); out.print("EGL version="); out.print(eglVersion);
|
||||
out.print(", "); out.print("EGL ready="); out.print(mEglReady);
|
||||
out.print(", "); out.print("has EglContext="); out.print(hasEglContext());
|
||||
out.print(", "); out.print("has EglSurface="); out.println(hasEglSurface());
|
||||
|
||||
int[] configs = getConfig();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('{');
|
||||
for (int egl : configs) {
|
||||
sb.append("0x").append(Integer.toHexString(egl)).append(",");
|
||||
}
|
||||
sb.setCharAt(sb.length() - 1, '}');
|
||||
out.print(prefix); out.print("EglConfig="); out.println(sb.toString());
|
||||
}
|
||||
}
|
||||
@@ -1,70 +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.wallpapers.gl;
|
||||
|
||||
import android.util.Size;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* A renderer which is responsible for making OpenGL calls to render a frame.
|
||||
*/
|
||||
public interface GLWallpaperRenderer {
|
||||
|
||||
/**
|
||||
* Check if the content to render is a WCG content.
|
||||
*/
|
||||
boolean isWcgContent();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* Ask renderer to report the surface size it needs.
|
||||
*/
|
||||
Size reportSurfaceSize();
|
||||
|
||||
/**
|
||||
* Called when no need to render any more.
|
||||
*/
|
||||
void finish();
|
||||
|
||||
/**
|
||||
* Called to dump current state.
|
||||
* @param prefix prefix.
|
||||
* @param fd fd.
|
||||
* @param out out.
|
||||
* @param args args.
|
||||
*/
|
||||
void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args);
|
||||
|
||||
}
|
||||
@@ -1,115 +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.wallpapers.gl;
|
||||
|
||||
import static android.opengl.GLES20.GL_FRAGMENT_SHADER;
|
||||
import static android.opengl.GLES20.GL_VERTEX_SHADER;
|
||||
import static android.opengl.GLES20.glAttachShader;
|
||||
import static android.opengl.GLES20.glCompileShader;
|
||||
import static android.opengl.GLES20.glCreateProgram;
|
||||
import static android.opengl.GLES20.glCreateShader;
|
||||
import static android.opengl.GLES20.glGetAttribLocation;
|
||||
import static android.opengl.GLES20.glGetUniformLocation;
|
||||
import static android.opengl.GLES20.glLinkProgram;
|
||||
import static android.opengl.GLES20.glShaderSource;
|
||||
import static android.opengl.GLES20.glUseProgram;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* This class takes charge of linking shader codes and then return a handle for OpenGL ES program.
|
||||
*/
|
||||
class ImageGLProgram {
|
||||
private static final String TAG = ImageGLProgram.class.getSimpleName();
|
||||
|
||||
private Context mContext;
|
||||
private int mProgramHandle;
|
||||
|
||||
ImageGLProgram(Context context) {
|
||||
mContext = context.getApplicationContext();
|
||||
}
|
||||
|
||||
private int loadShaderProgram(int vertexId, int fragmentId) {
|
||||
final String vertexSrc = getShaderResource(vertexId);
|
||||
final String fragmentSrc = getShaderResource(fragmentId);
|
||||
final int vertexHandle = getShaderHandle(GL_VERTEX_SHADER, vertexSrc);
|
||||
final int fragmentHandle = getShaderHandle(GL_FRAGMENT_SHADER, fragmentSrc);
|
||||
return getProgramHandle(vertexHandle, fragmentHandle);
|
||||
}
|
||||
|
||||
private String getShaderResource(int shaderId) {
|
||||
Resources res = mContext.getResources();
|
||||
StringBuilder code = new StringBuilder();
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(res.openRawResource(shaderId)))) {
|
||||
String nextLine;
|
||||
while ((nextLine = reader.readLine()) != null) {
|
||||
code.append(nextLine).append("\n");
|
||||
}
|
||||
} catch (IOException | Resources.NotFoundException ex) {
|
||||
Log.d(TAG, "Can not read the shader source", ex);
|
||||
code = null;
|
||||
}
|
||||
|
||||
return code == null ? "" : code.toString();
|
||||
}
|
||||
|
||||
private int getShaderHandle(int type, String src) {
|
||||
final int shader = glCreateShader(type);
|
||||
if (shader == 0) {
|
||||
Log.d(TAG, "Create shader failed, type=" + type);
|
||||
return 0;
|
||||
}
|
||||
glShaderSource(shader, src);
|
||||
glCompileShader(shader);
|
||||
return shader;
|
||||
}
|
||||
|
||||
private int getProgramHandle(int vertexHandle, int fragmentHandle) {
|
||||
final int program = glCreateProgram();
|
||||
if (program == 0) {
|
||||
Log.d(TAG, "Can not create OpenGL ES program");
|
||||
return 0;
|
||||
}
|
||||
|
||||
glAttachShader(program, vertexHandle);
|
||||
glAttachShader(program, fragmentHandle);
|
||||
glLinkProgram(program);
|
||||
return program;
|
||||
}
|
||||
|
||||
boolean useGLProgram(int vertexResId, int fragmentResId) {
|
||||
mProgramHandle = loadShaderProgram(vertexResId, fragmentResId);
|
||||
glUseProgram(mProgramHandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
int getAttributeHandle(String name) {
|
||||
return glGetAttribLocation(mProgramHandle, name);
|
||||
}
|
||||
|
||||
int getUniformHandle(String name) {
|
||||
return glGetUniformLocation(mProgramHandle, name);
|
||||
}
|
||||
}
|
||||
@@ -1,183 +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.wallpapers.gl;
|
||||
|
||||
import static android.opengl.GLES20.GL_FLOAT;
|
||||
import static android.opengl.GLES20.GL_LINEAR;
|
||||
import static android.opengl.GLES20.GL_TEXTURE0;
|
||||
import static android.opengl.GLES20.GL_TEXTURE_2D;
|
||||
import static android.opengl.GLES20.GL_TEXTURE_MAG_FILTER;
|
||||
import static android.opengl.GLES20.GL_TEXTURE_MIN_FILTER;
|
||||
import static android.opengl.GLES20.GL_TRIANGLES;
|
||||
import static android.opengl.GLES20.glActiveTexture;
|
||||
import static android.opengl.GLES20.glBindTexture;
|
||||
import static android.opengl.GLES20.glDrawArrays;
|
||||
import static android.opengl.GLES20.glEnableVertexAttribArray;
|
||||
import static android.opengl.GLES20.glGenTextures;
|
||||
import static android.opengl.GLES20.glTexParameteri;
|
||||
import static android.opengl.GLES20.glUniform1i;
|
||||
import static android.opengl.GLES20.glVertexAttribPointer;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.opengl.GLUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
/**
|
||||
* This class takes charge of the geometry data like vertices and texture coordinates.
|
||||
* It delivers these data to opengl runtime and triggers draw calls if necessary.
|
||||
*/
|
||||
class ImageGLWallpaper {
|
||||
private static final String TAG = ImageGLWallpaper.class.getSimpleName();
|
||||
|
||||
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;
|
||||
|
||||
// Vertices to define the square with 2 triangles.
|
||||
private static final float[] VERTICES = {
|
||||
-1.0f, -1.0f,
|
||||
+1.0f, -1.0f,
|
||||
+1.0f, +1.0f,
|
||||
+1.0f, +1.0f,
|
||||
-1.0f, +1.0f,
|
||||
-1.0f, -1.0f
|
||||
};
|
||||
|
||||
// Texture coordinates that maps to vertices.
|
||||
private static final float[] TEXTURES = {
|
||||
0f, 1f,
|
||||
1f, 1f,
|
||||
1f, 0f,
|
||||
1f, 0f,
|
||||
0f, 0f,
|
||||
0f, 1f
|
||||
};
|
||||
|
||||
private final FloatBuffer mVertexBuffer;
|
||||
private final FloatBuffer mTextureBuffer;
|
||||
private final ImageGLProgram mProgram;
|
||||
|
||||
private int mAttrPosition;
|
||||
private int mAttrTextureCoordinates;
|
||||
private int mUniTexture;
|
||||
private int mTextureId;
|
||||
|
||||
ImageGLWallpaper(ImageGLProgram program) {
|
||||
mProgram = program;
|
||||
|
||||
// Create an float array in opengles runtime (native) and put vertex data.
|
||||
mVertexBuffer = ByteBuffer.allocateDirect(VERTICES.length * BYTES_PER_FLOAT)
|
||||
.order(ByteOrder.nativeOrder())
|
||||
.asFloatBuffer();
|
||||
mVertexBuffer.put(VERTICES);
|
||||
mVertexBuffer.position(0);
|
||||
|
||||
// Create an float array in opengles runtime (native) and put texture data.
|
||||
mTextureBuffer = ByteBuffer.allocateDirect(TEXTURES.length * BYTES_PER_FLOAT)
|
||||
.order(ByteOrder.nativeOrder())
|
||||
.asFloatBuffer();
|
||||
mTextureBuffer.put(TEXTURES);
|
||||
mTextureBuffer.position(0);
|
||||
}
|
||||
|
||||
void setup(Bitmap bitmap) {
|
||||
setupAttributes();
|
||||
setupUniforms();
|
||||
setupTexture(bitmap);
|
||||
}
|
||||
|
||||
private void setupAttributes() {
|
||||
mAttrPosition = mProgram.getAttributeHandle(A_POSITION);
|
||||
mVertexBuffer.position(0);
|
||||
glVertexAttribPointer(mAttrPosition, POSITION_COMPONENT_COUNT, GL_FLOAT,
|
||||
false, 0, mVertexBuffer);
|
||||
glEnableVertexAttribArray(mAttrPosition);
|
||||
|
||||
mAttrTextureCoordinates = mProgram.getAttributeHandle(A_TEXTURE_COORDINATES);
|
||||
mTextureBuffer.position(0);
|
||||
glVertexAttribPointer(mAttrTextureCoordinates, TEXTURE_COMPONENT_COUNT, GL_FLOAT,
|
||||
false, 0, mTextureBuffer);
|
||||
glEnableVertexAttribArray(mAttrTextureCoordinates);
|
||||
}
|
||||
|
||||
private void setupUniforms() {
|
||||
mUniTexture = mProgram.getUniformHandle(U_TEXTURE);
|
||||
}
|
||||
|
||||
void draw() {
|
||||
glDrawArrays(GL_TRIANGLES, 0, VERTICES.length / 2);
|
||||
}
|
||||
|
||||
private void setupTexture(Bitmap bitmap) {
|
||||
final int[] tids = new int[1];
|
||||
|
||||
if (bitmap == null || bitmap.isRecycled()) {
|
||||
Log.w(TAG, "setupTexture: invalid bitmap");
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate one texture object and store the id in tids[0].
|
||||
glGenTextures(1, tids, 0);
|
||||
if (tids[0] == 0) {
|
||||
Log.w(TAG, "setupTexture: glGenTextures() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Bind a named texture to a target.
|
||||
glBindTexture(GL_TEXTURE_2D, tids[0]);
|
||||
// Load the bitmap data and copy it over into the texture object
|
||||
// that is currently bound.
|
||||
GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
|
||||
// Use bilinear texture filtering when minification.
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
// Use bilinear texture filtering when magnification.
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
mTextureId = tids[0];
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.w(TAG, "Failed uploading texture: " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
void useTexture() {
|
||||
// Set the active texture unit to texture unit 0.
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
// Bind the texture to this unit.
|
||||
glBindTexture(GL_TEXTURE_2D, mTextureId);
|
||||
// Let the texture sampler in fragment shader to read form this texture unit.
|
||||
glUniform1i(mUniTexture, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to dump current state.
|
||||
* @param prefix prefix.
|
||||
* @param fd fd.
|
||||
* @param out out.
|
||||
* @param args args.
|
||||
*/
|
||||
public void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
|
||||
}
|
||||
}
|
||||
@@ -1,195 +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.wallpapers.gl;
|
||||
|
||||
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.glViewport;
|
||||
|
||||
import android.app.WallpaperManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Rect;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.util.Size;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A GL renderer for image wallpaper.
|
||||
*/
|
||||
public class ImageWallpaperRenderer implements GLWallpaperRenderer {
|
||||
private static final String TAG = ImageWallpaperRenderer.class.getSimpleName();
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private final ImageGLProgram mProgram;
|
||||
private final ImageGLWallpaper mWallpaper;
|
||||
private final Rect mSurfaceSize = new Rect();
|
||||
private final WallpaperTexture mTexture;
|
||||
private Consumer<Bitmap> mOnBitmapUpdated;
|
||||
|
||||
public ImageWallpaperRenderer(Context context) {
|
||||
final WallpaperManager wpm = context.getSystemService(WallpaperManager.class);
|
||||
if (wpm == null) {
|
||||
Log.w(TAG, "WallpaperManager not available");
|
||||
}
|
||||
|
||||
mTexture = new WallpaperTexture(wpm);
|
||||
mProgram = new ImageGLProgram(context);
|
||||
mWallpaper = new ImageGLWallpaper(mProgram);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void setOnBitmapChanged(Consumer<Bitmap> c) {
|
||||
mOnBitmapUpdated = c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void use(Consumer<Bitmap> c) {
|
||||
mTexture.use(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWcgContent() {
|
||||
return mTexture.isWcgContent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceCreated() {
|
||||
glClearColor(0f, 0f, 0f, 1.0f);
|
||||
mProgram.useGLProgram(
|
||||
R.raw.image_wallpaper_vertex_shader, R.raw.image_wallpaper_fragment_shader);
|
||||
|
||||
mTexture.use(bitmap -> {
|
||||
if (bitmap == null) {
|
||||
Log.w(TAG, "reload texture failed!");
|
||||
} else if (mOnBitmapUpdated != null) {
|
||||
mOnBitmapUpdated.accept(bitmap);
|
||||
}
|
||||
mWallpaper.setup(bitmap);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceChanged(int width, int height) {
|
||||
glViewport(0, 0, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawFrame() {
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glViewport(0, 0, mSurfaceSize.width(), mSurfaceSize.height());
|
||||
mWallpaper.useTexture();
|
||||
mWallpaper.draw();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Size reportSurfaceSize() {
|
||||
mSurfaceSize.set(mTexture.getTextureDimensions());
|
||||
return new Size(mSurfaceSize.width(), mSurfaceSize.height());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
|
||||
out.print(prefix); out.print("mSurfaceSize="); out.print(mSurfaceSize);
|
||||
out.print(prefix); out.print("mWcgContent="); out.print(isWcgContent());
|
||||
mWallpaper.dump(prefix, fd, out, args);
|
||||
}
|
||||
|
||||
static class WallpaperTexture {
|
||||
private final AtomicInteger mRefCount;
|
||||
private final Rect mDimensions;
|
||||
private final WallpaperManager mWallpaperManager;
|
||||
private Bitmap mBitmap;
|
||||
private boolean mWcgContent;
|
||||
private boolean mTextureUsed;
|
||||
|
||||
private WallpaperTexture(WallpaperManager wallpaperManager) {
|
||||
mWallpaperManager = wallpaperManager;
|
||||
mRefCount = new AtomicInteger();
|
||||
mDimensions = new Rect();
|
||||
}
|
||||
|
||||
public void use(Consumer<Bitmap> consumer) {
|
||||
mRefCount.incrementAndGet();
|
||||
synchronized (mRefCount) {
|
||||
if (mBitmap == null) {
|
||||
mBitmap = mWallpaperManager.getBitmapAsUser(UserHandle.USER_CURRENT,
|
||||
false /* hardware */);
|
||||
mWcgContent = mWallpaperManager.wallpaperSupportsWcg(
|
||||
WallpaperManager.FLAG_SYSTEM);
|
||||
mWallpaperManager.forgetLoadedWallpaper();
|
||||
if (mBitmap != null) {
|
||||
mDimensions.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
|
||||
mTextureUsed = true;
|
||||
} else {
|
||||
Log.w(TAG, "Can't get bitmap");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (consumer != null) {
|
||||
consumer.accept(mBitmap);
|
||||
}
|
||||
synchronized (mRefCount) {
|
||||
final int count = mRefCount.decrementAndGet();
|
||||
if (count == 0 && mBitmap != null) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "WallpaperTexture: release 0x" + getHash()
|
||||
+ ", refCount=" + count);
|
||||
}
|
||||
mBitmap.recycle();
|
||||
mBitmap = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isWcgContent() {
|
||||
return mWcgContent;
|
||||
}
|
||||
|
||||
private String getHash() {
|
||||
return mBitmap != null ? Integer.toHexString(mBitmap.hashCode()) : "null";
|
||||
}
|
||||
|
||||
private Rect getTextureDimensions() {
|
||||
if (!mTextureUsed) {
|
||||
mDimensions.set(mWallpaperManager.peekBitmapDimensions());
|
||||
}
|
||||
return mDimensions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{" + getHash() + ", " + mRefCount.get() + "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,15 +19,13 @@ package com.android.systemui.wallpapers;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
|
||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -36,19 +34,13 @@ import static org.mockito.hamcrest.MockitoHamcrest.intThat;
|
||||
|
||||
import android.app.WallpaperManager;
|
||||
import android.content.Context;
|
||||
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.DisplayManager;
|
||||
import android.hardware.display.DisplayManagerGlobal;
|
||||
import android.os.Handler;
|
||||
import android.os.UserHandle;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.WindowManager;
|
||||
@@ -57,28 +49,21 @@ import android.view.WindowMetrics;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||
import com.android.systemui.util.time.FakeSystemClock;
|
||||
import com.android.systemui.wallpapers.gl.ImageWallpaperRenderer;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper
|
||||
public class ImageWallpaperTest extends SysuiTestCase {
|
||||
private static final int LOW_BMP_WIDTH = 128;
|
||||
private static final int LOW_BMP_HEIGHT = 128;
|
||||
private static final int INVALID_BMP_WIDTH = 1;
|
||||
private static final int INVALID_BMP_HEIGHT = 1;
|
||||
private static final int DISPLAY_WIDTH = 1920;
|
||||
private static final int DISPLAY_HEIGHT = 1080;
|
||||
|
||||
@@ -99,19 +84,9 @@ public class ImageWallpaperTest extends SysuiTestCase {
|
||||
|
||||
@Mock
|
||||
private Bitmap mWallpaperBitmap;
|
||||
private int mBitmapWidth = 1;
|
||||
private int mBitmapHeight = 1;
|
||||
|
||||
@Mock
|
||||
private Handler mHandler;
|
||||
@Mock
|
||||
private FeatureFlags mFeatureFlags;
|
||||
|
||||
FakeSystemClock mFakeSystemClock = new FakeSystemClock();
|
||||
FakeExecutor mFakeBackgroundExecutor = new FakeExecutor(mFakeSystemClock);
|
||||
|
||||
private CountDownLatch mEventCountdown;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
allowTestableLooperAsMainThread();
|
||||
@@ -131,12 +106,8 @@ public class ImageWallpaperTest extends SysuiTestCase {
|
||||
// set up bitmap
|
||||
when(mWallpaperBitmap.getColorSpace()).thenReturn(ColorSpace.get(ColorSpace.Named.SRGB));
|
||||
when(mWallpaperBitmap.getConfig()).thenReturn(Bitmap.Config.ARGB_8888);
|
||||
when(mWallpaperBitmap.getWidth()).thenReturn(mBitmapWidth);
|
||||
when(mWallpaperBitmap.getHeight()).thenReturn(mBitmapHeight);
|
||||
|
||||
// set up wallpaper manager
|
||||
when(mWallpaperManager.peekBitmapDimensions())
|
||||
.thenReturn(new Rect(0, 0, mBitmapWidth, mBitmapHeight));
|
||||
when(mWallpaperManager.getBitmapAsUser(eq(UserHandle.USER_CURRENT), anyBoolean()))
|
||||
.thenReturn(mWallpaperBitmap);
|
||||
when(mMockContext.getSystemService(WallpaperManager.class)).thenReturn(mWallpaperManager);
|
||||
@@ -144,104 +115,62 @@ public class ImageWallpaperTest extends SysuiTestCase {
|
||||
// set up surface
|
||||
when(mSurfaceHolder.getSurface()).thenReturn(mSurface);
|
||||
doNothing().when(mSurface).hwuiDestroy();
|
||||
|
||||
// TODO remove code below. Outdated, used in only in old GL tests (that are ignored)
|
||||
Resources resources = mock(Resources.class);
|
||||
when(resources.getConfiguration()).thenReturn(mock(Configuration.class));
|
||||
when(mMockContext.getResources()).thenReturn(resources);
|
||||
DisplayInfo displayInfo = new DisplayInfo();
|
||||
displayInfo.logicalWidth = DISPLAY_WIDTH;
|
||||
displayInfo.logicalHeight = DISPLAY_HEIGHT;
|
||||
when(mMockContext.getDisplay()).thenReturn(
|
||||
new Display(mock(DisplayManagerGlobal.class), 0, displayInfo, (Resources) null));
|
||||
}
|
||||
|
||||
private void setBitmapDimensions(int bitmapWidth, int bitmapHeight) {
|
||||
mBitmapWidth = bitmapWidth;
|
||||
mBitmapHeight = bitmapHeight;
|
||||
}
|
||||
|
||||
private ImageWallpaper createImageWallpaper() {
|
||||
return new ImageWallpaper(mFeatureFlags, mFakeBackgroundExecutor) {
|
||||
@Override
|
||||
public Engine onCreateEngine() {
|
||||
return new GLEngine(mHandler) {
|
||||
@Override
|
||||
public Context getDisplayContext() {
|
||||
return mMockContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SurfaceHolder getSurfaceHolder() {
|
||||
return mSurfaceHolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFixedSizeAllowed(boolean allowed) {
|
||||
super.setFixedSizeAllowed(allowed);
|
||||
assertWithMessage("mFixedSizeAllowed should be true").that(
|
||||
allowed).isTrue();
|
||||
mEventCountdown.countDown();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
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.
|
||||
verifySurfaceSize(DISPLAY_WIDTH /* bmpWidth */,
|
||||
DISPLAY_WIDTH /* bmpHeight */,
|
||||
DISPLAY_WIDTH /* surfaceWidth */,
|
||||
DISPLAY_WIDTH /* surfaceHeight */);
|
||||
int bitmapSide = DISPLAY_WIDTH;
|
||||
testSurfaceHelper(
|
||||
bitmapSide /* bitmapWidth */,
|
||||
bitmapSide /* bitmapHeight */,
|
||||
bitmapSide /* expectedSurfaceWidth */,
|
||||
bitmapSide /* expectedSurfaceHeight */);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
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.
|
||||
verifySurfaceSize(LOW_BMP_WIDTH /* bmpWidth */,
|
||||
LOW_BMP_HEIGHT /* bmpHeight */,
|
||||
LOW_BMP_WIDTH /* surfaceWidth */,
|
||||
LOW_BMP_HEIGHT /* surfaceHeight */);
|
||||
testSurfaceHelper(LOW_BMP_WIDTH /* bitmapWidth */,
|
||||
LOW_BMP_HEIGHT /* bitmapHeight */,
|
||||
LOW_BMP_WIDTH /* expectedSurfaceWidth */,
|
||||
LOW_BMP_HEIGHT /* expectedSurfaceHeight */);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
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.
|
||||
verifySurfaceSize(INVALID_BMP_WIDTH /* bmpWidth */,
|
||||
INVALID_BMP_HEIGHT /* bmpHeight */,
|
||||
ImageWallpaper.GLEngine.MIN_SURFACE_WIDTH /* surfaceWidth */,
|
||||
ImageWallpaper.GLEngine.MIN_SURFACE_HEIGHT /* surfaceHeight */);
|
||||
|
||||
// test that the surface is always at least MIN_SURFACE_WIDTH x MIN_SURFACE_HEIGHT
|
||||
testMinSurfaceHelper(8, 8);
|
||||
testMinSurfaceHelper(100, 2000);
|
||||
testMinSurfaceHelper(200, 1);
|
||||
}
|
||||
|
||||
private void verifySurfaceSize(int bmpWidth, int bmpHeight,
|
||||
int surfaceWidth, int surfaceHeight) {
|
||||
ImageWallpaper.GLEngine wallpaperEngine =
|
||||
(ImageWallpaper.GLEngine) createImageWallpaper().onCreateEngine();
|
||||
@Test
|
||||
public void testLoadDrawAndUnloadBitmap() {
|
||||
setBitmapDimensions(LOW_BMP_WIDTH, LOW_BMP_HEIGHT);
|
||||
|
||||
ImageWallpaper.GLEngine engineSpy = spy(wallpaperEngine);
|
||||
ImageWallpaper.CanvasEngine spyEngine = getSpyEngine();
|
||||
spyEngine.onCreate(mSurfaceHolder);
|
||||
spyEngine.onSurfaceRedrawNeeded(mSurfaceHolder);
|
||||
assertThat(mFakeBackgroundExecutor.numPending()).isAtLeast(1);
|
||||
|
||||
setBitmapDimensions(bmpWidth, bmpHeight);
|
||||
int n = 0;
|
||||
while (mFakeBackgroundExecutor.numPending() >= 1) {
|
||||
n++;
|
||||
assertThat(n).isAtMost(10);
|
||||
mFakeBackgroundExecutor.runNextReady();
|
||||
mFakeSystemClock.advanceTime(1000);
|
||||
}
|
||||
|
||||
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);
|
||||
verify(spyEngine, times(1)).drawFrameOnCanvas(mWallpaperBitmap);
|
||||
assertThat(spyEngine.isBitmapLoaded()).isFalse();
|
||||
}
|
||||
|
||||
|
||||
private ImageWallpaper createImageWallpaperCanvas() {
|
||||
return new ImageWallpaper(mFeatureFlags, mFakeBackgroundExecutor) {
|
||||
private ImageWallpaper createImageWallpaper() {
|
||||
return new ImageWallpaper(mFakeBackgroundExecutor) {
|
||||
@Override
|
||||
public Engine onCreateEngine() {
|
||||
return new CanvasEngine() {
|
||||
@@ -267,7 +196,7 @@ public class ImageWallpaperTest extends SysuiTestCase {
|
||||
}
|
||||
|
||||
private ImageWallpaper.CanvasEngine getSpyEngine() {
|
||||
ImageWallpaper imageWallpaper = createImageWallpaperCanvas();
|
||||
ImageWallpaper imageWallpaper = createImageWallpaper();
|
||||
ImageWallpaper.CanvasEngine engine =
|
||||
(ImageWallpaper.CanvasEngine) imageWallpaper.onCreateEngine();
|
||||
ImageWallpaper.CanvasEngine spyEngine = spy(engine);
|
||||
@@ -280,48 +209,32 @@ public class ImageWallpaperTest extends SysuiTestCase {
|
||||
return spyEngine;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinSurface() {
|
||||
|
||||
// test that the surface is always at least MIN_SURFACE_WIDTH x MIN_SURFACE_HEIGHT
|
||||
testMinSurfaceHelper(8, 8);
|
||||
testMinSurfaceHelper(100, 2000);
|
||||
testMinSurfaceHelper(200, 1);
|
||||
private void setBitmapDimensions(int bitmapWidth, int bitmapHeight) {
|
||||
when(mWallpaperManager.peekBitmapDimensions())
|
||||
.thenReturn(new Rect(0, 0, bitmapWidth, bitmapHeight));
|
||||
when(mWallpaperBitmap.getWidth()).thenReturn(bitmapWidth);
|
||||
when(mWallpaperBitmap.getHeight()).thenReturn(bitmapHeight);
|
||||
}
|
||||
|
||||
private void testMinSurfaceHelper(int bitmapWidth, int bitmapHeight) {
|
||||
testSurfaceHelper(bitmapWidth, bitmapHeight,
|
||||
Math.max(ImageWallpaper.CanvasEngine.MIN_SURFACE_WIDTH, bitmapWidth),
|
||||
Math.max(ImageWallpaper.CanvasEngine.MIN_SURFACE_HEIGHT, bitmapHeight));
|
||||
}
|
||||
|
||||
private void testSurfaceHelper(int bitmapWidth, int bitmapHeight,
|
||||
int expectedSurfaceWidth, int expectedSurfaceHeight) {
|
||||
|
||||
clearInvocations(mSurfaceHolder);
|
||||
setBitmapDimensions(bitmapWidth, bitmapHeight);
|
||||
|
||||
ImageWallpaper imageWallpaper = createImageWallpaperCanvas();
|
||||
ImageWallpaper imageWallpaper = createImageWallpaper();
|
||||
ImageWallpaper.CanvasEngine engine =
|
||||
(ImageWallpaper.CanvasEngine) imageWallpaper.onCreateEngine();
|
||||
engine.onCreate(mSurfaceHolder);
|
||||
|
||||
verify(mSurfaceHolder, times(1)).setFixedSize(
|
||||
intThat(greaterThanOrEqualTo(ImageWallpaper.CanvasEngine.MIN_SURFACE_WIDTH)),
|
||||
intThat(greaterThanOrEqualTo(ImageWallpaper.CanvasEngine.MIN_SURFACE_HEIGHT)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadDrawAndUnloadBitmap() {
|
||||
setBitmapDimensions(LOW_BMP_WIDTH, LOW_BMP_HEIGHT);
|
||||
|
||||
ImageWallpaper.CanvasEngine spyEngine = getSpyEngine();
|
||||
spyEngine.onCreate(mSurfaceHolder);
|
||||
spyEngine.onSurfaceRedrawNeeded(mSurfaceHolder);
|
||||
assertThat(mFakeBackgroundExecutor.numPending()).isAtLeast(1);
|
||||
|
||||
int n = 0;
|
||||
while (mFakeBackgroundExecutor.numPending() >= 1) {
|
||||
n++;
|
||||
assertThat(n).isAtMost(10);
|
||||
mFakeBackgroundExecutor.runNextReady();
|
||||
mFakeSystemClock.advanceTime(1000);
|
||||
}
|
||||
|
||||
verify(spyEngine, times(1)).drawFrameOnCanvas(mWallpaperBitmap);
|
||||
assertThat(spyEngine.isBitmapLoaded()).isFalse();
|
||||
intThat(equalTo(expectedSurfaceWidth)),
|
||||
intThat(equalTo(expectedSurfaceHeight)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.wallpapers.canvas;
|
||||
package com.android.systemui.wallpapers;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
@@ -1,128 +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.wallpapers.gl;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.atMost;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.graphics.PixelFormat;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceSession;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
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;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.Spy;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@Ignore
|
||||
public class EglHelperTest extends SysuiTestCase {
|
||||
|
||||
@Spy
|
||||
private EglHelper mEglHelper;
|
||||
|
||||
@Mock
|
||||
private SurfaceHolder mSurfaceHolder;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
prepareSurface();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
mSurfaceHolder.getSurface().destroy();
|
||||
mSurfaceHolder = null;
|
||||
}
|
||||
|
||||
private void prepareSurface() {
|
||||
final SurfaceSession session = new SurfaceSession();
|
||||
final SurfaceControl control = new SurfaceControl.Builder(session)
|
||||
.setName("Test")
|
||||
.setBufferSize(100, 100)
|
||||
.setFormat(PixelFormat.RGB_888)
|
||||
.build();
|
||||
final Surface surface = new Surface();
|
||||
surface.copyFrom(control);
|
||||
when(mSurfaceHolder.getSurface()).thenReturn(surface);
|
||||
assertThat(mSurfaceHolder.getSurface()).isNotNull();
|
||||
assertThat(mSurfaceHolder.getSurface().isValid()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
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());
|
||||
}
|
||||
|
||||
@Test
|
||||
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";
|
||||
doReturn(true).when(mEglHelper).checkExtensionCapability("EGL_KHR_gl_colorspace");
|
||||
ArgumentCaptor<int[]> ac = ArgumentCaptor.forClass(int[].class);
|
||||
// {EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT, EGL_NONE}
|
||||
final int[] expectedArgument = new int[] {0x309D, 0x3490, 0x3038};
|
||||
|
||||
mEglHelper.init(mSurfaceHolder, true /* wideColorGamut */);
|
||||
verify(mEglHelper)
|
||||
.askCreatingEglWindowSurface(any(SurfaceHolder.class), ac.capture(), anyInt());
|
||||
assertThat(ac.getValue()).isNotNull();
|
||||
assertThat(ac.getValue()).isEqualTo(expectedArgument);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testFinish_shouldNotCrash() {
|
||||
mEglHelper.terminateEglDisplay();
|
||||
assertThat(mEglHelper.hasEglDisplay()).isFalse();
|
||||
assertThat(mEglHelper.hasEglSurface()).isFalse();
|
||||
assertThat(mEglHelper.hasEglContext()).isFalse();
|
||||
|
||||
mEglHelper.finish();
|
||||
verify(mEglHelper, never()).destroyEglContext();
|
||||
verify(mEglHelper, never()).destroyEglSurface();
|
||||
verify(mEglHelper, atMost(1)).terminateEglDisplay();
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.wallpapers.gl;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
import android.app.WallpaperManager;
|
||||
import android.app.WallpaperManager.ColorManagementProxy;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.ColorSpace;
|
||||
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.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
||||
@Ignore
|
||||
public class ImageWallpaperRendererTest extends SysuiTestCase {
|
||||
|
||||
private WallpaperManager mWpmSpy;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
final WallpaperManager wpm = mContext.getSystemService(WallpaperManager.class);
|
||||
mWpmSpy = spy(wpm);
|
||||
mContext.addMockSystemService(WallpaperManager.class, mWpmSpy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWcgContent() throws IOException {
|
||||
final Bitmap srgbBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
|
||||
final Bitmap p3Bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888,
|
||||
false /* hasAlpha */, ColorSpace.get(ColorSpace.Named.DISPLAY_P3));
|
||||
|
||||
final ColorManagementProxy proxy = new ColorManagementProxy(mContext);
|
||||
final ColorManagementProxy cmProxySpy = spy(proxy);
|
||||
final Set<ColorSpace> supportedWideGamuts = new HashSet<>();
|
||||
supportedWideGamuts.add(ColorSpace.get(ColorSpace.Named.DISPLAY_P3));
|
||||
|
||||
try {
|
||||
doReturn(true).when(mWpmSpy).shouldEnableWideColorGamut();
|
||||
doReturn(cmProxySpy).when(mWpmSpy).getColorManagementProxy();
|
||||
doReturn(supportedWideGamuts).when(cmProxySpy).getSupportedColorSpaces();
|
||||
|
||||
mWpmSpy.setBitmap(p3Bitmap);
|
||||
ImageWallpaperRenderer rendererP3 = new ImageWallpaperRenderer(mContext);
|
||||
rendererP3.reportSurfaceSize();
|
||||
assertThat(rendererP3.isWcgContent()).isTrue();
|
||||
|
||||
mWpmSpy.setBitmap(srgbBitmap);
|
||||
ImageWallpaperRenderer renderer = new ImageWallpaperRenderer(mContext);
|
||||
assertThat(renderer.isWcgContent()).isFalse();
|
||||
} finally {
|
||||
srgbBitmap.recycle();
|
||||
p3Bitmap.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user