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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
coreApp="true">
|
coreApp="true">
|
||||||
|
|
||||||
<!-- Using OpenGL ES 2.0 -->
|
|
||||||
<uses-feature
|
|
||||||
android:glEsVersion="0x00020000"
|
|
||||||
android:required="true" />
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
|
|
||||||
<!-- Used to read wallpaper -->
|
<!-- 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
|
// TODO(b/254512848): Tracking Bug
|
||||||
val REGION_SAMPLING = unreleasedFlag(801, "region_sampling", teamfood = true)
|
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
|
// 803 - screen contents translation
|
||||||
// TODO(b/254513187): Tracking Bug
|
// TODO(b/254513187): Tracking Bug
|
||||||
val SCREEN_CONTENTS_TRANSLATION = unreleasedFlag(803, "screen_contents_translation")
|
val SCREEN_CONTENTS_TRANSLATION = unreleasedFlag(803, "screen_contents_translation")
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package com.android.systemui.wallpapers;
|
package com.android.systemui.wallpapers;
|
||||||
|
|
||||||
import static com.android.systemui.flags.Flags.USE_CANVAS_RENDERER;
|
|
||||||
|
|
||||||
import android.app.WallpaperColors;
|
import android.app.WallpaperColors;
|
||||||
import android.app.WallpaperManager;
|
import android.app.WallpaperManager;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
@@ -27,16 +25,10 @@ import android.graphics.Rect;
|
|||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import android.hardware.display.DisplayManager;
|
import android.hardware.display.DisplayManager;
|
||||||
import android.hardware.display.DisplayManager.DisplayListener;
|
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.Trace;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.service.wallpaper.WallpaperService;
|
import android.service.wallpaper.WallpaperService;
|
||||||
import android.util.ArraySet;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.MathUtils;
|
|
||||||
import android.util.Size;
|
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import android.view.SurfaceHolder;
|
import android.view.SurfaceHolder;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
@@ -45,16 +37,10 @@ import androidx.annotation.NonNull;
|
|||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.systemui.dagger.qualifiers.Background;
|
import com.android.systemui.dagger.qualifiers.Background;
|
||||||
import com.android.systemui.flags.FeatureFlags;
|
|
||||||
import com.android.systemui.util.concurrency.DelayableExecutor;
|
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.FileDescriptor;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@@ -64,455 +50,32 @@ import javax.inject.Inject;
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings({"UnusedDeclaration"})
|
@SuppressWarnings({"UnusedDeclaration"})
|
||||||
public class ImageWallpaper extends WallpaperService {
|
public class ImageWallpaper extends WallpaperService {
|
||||||
|
|
||||||
private static final String TAG = ImageWallpaper.class.getSimpleName();
|
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 static final boolean DEBUG = false;
|
||||||
|
|
||||||
private final ArrayList<RectF> mLocalColorsToAdd = new ArrayList<>();
|
// keep track of the number of pages of the launcher for local color extraction purposes
|
||||||
private final ArraySet<RectF> mColorAreas = new ArraySet<>();
|
|
||||||
private volatile int mPages = 1;
|
private volatile int mPages = 1;
|
||||||
private boolean mPagesComputed = false;
|
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
|
@Background
|
||||||
private final DelayableExecutor mBackgroundExecutor;
|
private final DelayableExecutor mBackgroundExecutor;
|
||||||
|
|
||||||
|
// wait at least this duration before unloading the bitmap
|
||||||
private static final int DELAY_UNLOAD_BITMAP = 2000;
|
private static final int DELAY_UNLOAD_BITMAP = 2000;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ImageWallpaper(FeatureFlags featureFlags,
|
public ImageWallpaper(@Background DelayableExecutor backgroundExecutor) {
|
||||||
@Background DelayableExecutor backgroundExecutor) {
|
|
||||||
super();
|
super();
|
||||||
mFeatureFlags = featureFlags;
|
|
||||||
mBackgroundExecutor = backgroundExecutor;
|
mBackgroundExecutor = backgroundExecutor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate() {
|
|
||||||
super.onCreate();
|
|
||||||
mWorker = new HandlerThread(TAG);
|
|
||||||
mWorker.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Engine onCreateEngine() {
|
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 {
|
class CanvasEngine extends WallpaperService.Engine implements DisplayListener {
|
||||||
private WallpaperManager mWallpaperManager;
|
private WallpaperManager mWallpaperManager;
|
||||||
private final WallpaperLocalColorExtractor mWallpaperLocalColorExtractor;
|
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
|
// be loaded, we will go into a cycle. Don't do a build where the
|
||||||
// default wallpaper can't be loaded.
|
// default wallpaper can't be loaded.
|
||||||
Log.w(TAG, "Unable to load wallpaper!", exception);
|
Log.w(TAG, "Unable to load wallpaper!", exception);
|
||||||
try {
|
mWallpaperManager.clearWallpaper(
|
||||||
mWallpaperManager.clear(WallpaperManager.FLAG_SYSTEM);
|
WallpaperManager.FLAG_SYSTEM, UserHandle.USER_CURRENT);
|
||||||
} catch (IOException ex) {
|
|
||||||
// now we're really screwed.
|
|
||||||
Log.w(TAG, "Unable reset to default wallpaper!", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
bitmap = mWallpaperManager.getBitmapAsUser(UserHandle.USER_CURRENT, false);
|
bitmap = mWallpaperManager.getBitmapAsUser(UserHandle.USER_CURRENT, false);
|
||||||
} catch (RuntimeException | OutOfMemoryError e) {
|
} catch (RuntimeException | OutOfMemoryError e) {
|
||||||
@@ -868,7 +426,6 @@ public class ImageWallpaper extends WallpaperService {
|
|||||||
mWallpaperLocalColorExtractor.setDisplayDimensions(window.width(), window.height());
|
mWallpaperLocalColorExtractor.setDisplayDimensions(window.width(), window.height());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
|
protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
|
||||||
super.dump(prefix, fd, out, 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.app.WallpaperColors;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
@@ -31,7 +31,6 @@ import androidx.annotation.VisibleForTesting;
|
|||||||
|
|
||||||
import com.android.systemui.dagger.qualifiers.Background;
|
import com.android.systemui.dagger.qualifiers.Background;
|
||||||
import com.android.systemui.util.Assert;
|
import com.android.systemui.util.Assert;
|
||||||
import com.android.systemui.wallpapers.ImageWallpaper;
|
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.PrintWriter;
|
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.assertThat;
|
||||||
import static com.google.common.truth.Truth.assertWithMessage;
|
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.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
import static org.mockito.Mockito.clearInvocations;
|
import static org.mockito.Mockito.clearInvocations;
|
||||||
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.doAnswer;
|
||||||
import static org.mockito.Mockito.doNothing;
|
import static org.mockito.Mockito.doNothing;
|
||||||
import static org.mockito.Mockito.doReturn;
|
|
||||||
import static org.mockito.Mockito.eq;
|
import static org.mockito.Mockito.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
@@ -36,19 +34,13 @@ import static org.mockito.hamcrest.MockitoHamcrest.intThat;
|
|||||||
|
|
||||||
import android.app.WallpaperManager;
|
import android.app.WallpaperManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.ColorSpace;
|
import android.graphics.ColorSpace;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.hardware.display.DisplayManager;
|
import android.hardware.display.DisplayManager;
|
||||||
import android.hardware.display.DisplayManagerGlobal;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.testing.AndroidTestingRunner;
|
import android.testing.AndroidTestingRunner;
|
||||||
import android.testing.TestableLooper;
|
import android.testing.TestableLooper;
|
||||||
import android.view.Display;
|
|
||||||
import android.view.DisplayInfo;
|
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import android.view.SurfaceHolder;
|
import android.view.SurfaceHolder;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
@@ -57,28 +49,21 @@ import android.view.WindowMetrics;
|
|||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
import com.android.systemui.SysuiTestCase;
|
import com.android.systemui.SysuiTestCase;
|
||||||
import com.android.systemui.flags.FeatureFlags;
|
|
||||||
import com.android.systemui.util.concurrency.FakeExecutor;
|
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||||
import com.android.systemui.util.time.FakeSystemClock;
|
import com.android.systemui.util.time.FakeSystemClock;
|
||||||
import com.android.systemui.wallpapers.gl.ImageWallpaperRenderer;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
|
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(AndroidTestingRunner.class)
|
@RunWith(AndroidTestingRunner.class)
|
||||||
@TestableLooper.RunWithLooper
|
@TestableLooper.RunWithLooper
|
||||||
public class ImageWallpaperTest extends SysuiTestCase {
|
public class ImageWallpaperTest extends SysuiTestCase {
|
||||||
private static final int LOW_BMP_WIDTH = 128;
|
private static final int LOW_BMP_WIDTH = 128;
|
||||||
private static final int LOW_BMP_HEIGHT = 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_WIDTH = 1920;
|
||||||
private static final int DISPLAY_HEIGHT = 1080;
|
private static final int DISPLAY_HEIGHT = 1080;
|
||||||
|
|
||||||
@@ -99,19 +84,9 @@ public class ImageWallpaperTest extends SysuiTestCase {
|
|||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private Bitmap mWallpaperBitmap;
|
private Bitmap mWallpaperBitmap;
|
||||||
private int mBitmapWidth = 1;
|
|
||||||
private int mBitmapHeight = 1;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private Handler mHandler;
|
|
||||||
@Mock
|
|
||||||
private FeatureFlags mFeatureFlags;
|
|
||||||
|
|
||||||
FakeSystemClock mFakeSystemClock = new FakeSystemClock();
|
FakeSystemClock mFakeSystemClock = new FakeSystemClock();
|
||||||
FakeExecutor mFakeBackgroundExecutor = new FakeExecutor(mFakeSystemClock);
|
FakeExecutor mFakeBackgroundExecutor = new FakeExecutor(mFakeSystemClock);
|
||||||
|
|
||||||
private CountDownLatch mEventCountdown;
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
allowTestableLooperAsMainThread();
|
allowTestableLooperAsMainThread();
|
||||||
@@ -131,12 +106,8 @@ public class ImageWallpaperTest extends SysuiTestCase {
|
|||||||
// set up bitmap
|
// set up bitmap
|
||||||
when(mWallpaperBitmap.getColorSpace()).thenReturn(ColorSpace.get(ColorSpace.Named.SRGB));
|
when(mWallpaperBitmap.getColorSpace()).thenReturn(ColorSpace.get(ColorSpace.Named.SRGB));
|
||||||
when(mWallpaperBitmap.getConfig()).thenReturn(Bitmap.Config.ARGB_8888);
|
when(mWallpaperBitmap.getConfig()).thenReturn(Bitmap.Config.ARGB_8888);
|
||||||
when(mWallpaperBitmap.getWidth()).thenReturn(mBitmapWidth);
|
|
||||||
when(mWallpaperBitmap.getHeight()).thenReturn(mBitmapHeight);
|
|
||||||
|
|
||||||
// set up wallpaper manager
|
// set up wallpaper manager
|
||||||
when(mWallpaperManager.peekBitmapDimensions())
|
|
||||||
.thenReturn(new Rect(0, 0, mBitmapWidth, mBitmapHeight));
|
|
||||||
when(mWallpaperManager.getBitmapAsUser(eq(UserHandle.USER_CURRENT), anyBoolean()))
|
when(mWallpaperManager.getBitmapAsUser(eq(UserHandle.USER_CURRENT), anyBoolean()))
|
||||||
.thenReturn(mWallpaperBitmap);
|
.thenReturn(mWallpaperBitmap);
|
||||||
when(mMockContext.getSystemService(WallpaperManager.class)).thenReturn(mWallpaperManager);
|
when(mMockContext.getSystemService(WallpaperManager.class)).thenReturn(mWallpaperManager);
|
||||||
@@ -144,104 +115,62 @@ public class ImageWallpaperTest extends SysuiTestCase {
|
|||||||
// set up surface
|
// set up surface
|
||||||
when(mSurfaceHolder.getSurface()).thenReturn(mSurface);
|
when(mSurfaceHolder.getSurface()).thenReturn(mSurface);
|
||||||
doNothing().when(mSurface).hwuiDestroy();
|
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
|
@Test
|
||||||
@Ignore
|
|
||||||
public void testBitmapWallpaper_normal() {
|
public void testBitmapWallpaper_normal() {
|
||||||
// Will use a image wallpaper with dimensions DISPLAY_WIDTH x DISPLAY_WIDTH.
|
// 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.
|
// Then we expect the surface size will be also DISPLAY_WIDTH x DISPLAY_WIDTH.
|
||||||
verifySurfaceSize(DISPLAY_WIDTH /* bmpWidth */,
|
int bitmapSide = DISPLAY_WIDTH;
|
||||||
DISPLAY_WIDTH /* bmpHeight */,
|
testSurfaceHelper(
|
||||||
DISPLAY_WIDTH /* surfaceWidth */,
|
bitmapSide /* bitmapWidth */,
|
||||||
DISPLAY_WIDTH /* surfaceHeight */);
|
bitmapSide /* bitmapHeight */,
|
||||||
|
bitmapSide /* expectedSurfaceWidth */,
|
||||||
|
bitmapSide /* expectedSurfaceHeight */);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
|
||||||
public void testBitmapWallpaper_low_resolution() {
|
public void testBitmapWallpaper_low_resolution() {
|
||||||
// Will use a image wallpaper with dimensions BMP_WIDTH x BMP_HEIGHT.
|
// 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.
|
// Then we expect the surface size will be also BMP_WIDTH x BMP_HEIGHT.
|
||||||
verifySurfaceSize(LOW_BMP_WIDTH /* bmpWidth */,
|
testSurfaceHelper(LOW_BMP_WIDTH /* bitmapWidth */,
|
||||||
LOW_BMP_HEIGHT /* bmpHeight */,
|
LOW_BMP_HEIGHT /* bitmapHeight */,
|
||||||
LOW_BMP_WIDTH /* surfaceWidth */,
|
LOW_BMP_WIDTH /* expectedSurfaceWidth */,
|
||||||
LOW_BMP_HEIGHT /* surfaceHeight */);
|
LOW_BMP_HEIGHT /* expectedSurfaceHeight */);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
|
||||||
public void testBitmapWallpaper_too_small() {
|
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.
|
// test that the surface is always at least MIN_SURFACE_WIDTH x MIN_SURFACE_HEIGHT
|
||||||
verifySurfaceSize(INVALID_BMP_WIDTH /* bmpWidth */,
|
testMinSurfaceHelper(8, 8);
|
||||||
INVALID_BMP_HEIGHT /* bmpHeight */,
|
testMinSurfaceHelper(100, 2000);
|
||||||
ImageWallpaper.GLEngine.MIN_SURFACE_WIDTH /* surfaceWidth */,
|
testMinSurfaceHelper(200, 1);
|
||||||
ImageWallpaper.GLEngine.MIN_SURFACE_HEIGHT /* surfaceHeight */);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifySurfaceSize(int bmpWidth, int bmpHeight,
|
@Test
|
||||||
int surfaceWidth, int surfaceHeight) {
|
public void testLoadDrawAndUnloadBitmap() {
|
||||||
ImageWallpaper.GLEngine wallpaperEngine =
|
setBitmapDimensions(LOW_BMP_WIDTH, LOW_BMP_HEIGHT);
|
||||||
(ImageWallpaper.GLEngine) createImageWallpaper().onCreateEngine();
|
|
||||||
|
|
||||||
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);
|
verify(spyEngine, times(1)).drawFrameOnCanvas(mWallpaperBitmap);
|
||||||
doReturn(renderer).when(engineSpy).getRendererInstance();
|
assertThat(spyEngine.isBitmapLoaded()).isFalse();
|
||||||
engineSpy.onCreate(engineSpy.getSurfaceHolder());
|
|
||||||
|
|
||||||
verify(mSurfaceHolder, times(1)).setFixedSize(surfaceWidth, surfaceHeight);
|
|
||||||
assertWithMessage("setFixedSizeAllowed should have been called.").that(
|
|
||||||
mEventCountdown.getCount()).isEqualTo(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ImageWallpaper createImageWallpaper() {
|
||||||
private ImageWallpaper createImageWallpaperCanvas() {
|
return new ImageWallpaper(mFakeBackgroundExecutor) {
|
||||||
return new ImageWallpaper(mFeatureFlags, mFakeBackgroundExecutor) {
|
|
||||||
@Override
|
@Override
|
||||||
public Engine onCreateEngine() {
|
public Engine onCreateEngine() {
|
||||||
return new CanvasEngine() {
|
return new CanvasEngine() {
|
||||||
@@ -267,7 +196,7 @@ public class ImageWallpaperTest extends SysuiTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ImageWallpaper.CanvasEngine getSpyEngine() {
|
private ImageWallpaper.CanvasEngine getSpyEngine() {
|
||||||
ImageWallpaper imageWallpaper = createImageWallpaperCanvas();
|
ImageWallpaper imageWallpaper = createImageWallpaper();
|
||||||
ImageWallpaper.CanvasEngine engine =
|
ImageWallpaper.CanvasEngine engine =
|
||||||
(ImageWallpaper.CanvasEngine) imageWallpaper.onCreateEngine();
|
(ImageWallpaper.CanvasEngine) imageWallpaper.onCreateEngine();
|
||||||
ImageWallpaper.CanvasEngine spyEngine = spy(engine);
|
ImageWallpaper.CanvasEngine spyEngine = spy(engine);
|
||||||
@@ -280,48 +209,32 @@ public class ImageWallpaperTest extends SysuiTestCase {
|
|||||||
return spyEngine;
|
return spyEngine;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
private void setBitmapDimensions(int bitmapWidth, int bitmapHeight) {
|
||||||
public void testMinSurface() {
|
when(mWallpaperManager.peekBitmapDimensions())
|
||||||
|
.thenReturn(new Rect(0, 0, bitmapWidth, bitmapHeight));
|
||||||
// test that the surface is always at least MIN_SURFACE_WIDTH x MIN_SURFACE_HEIGHT
|
when(mWallpaperBitmap.getWidth()).thenReturn(bitmapWidth);
|
||||||
testMinSurfaceHelper(8, 8);
|
when(mWallpaperBitmap.getHeight()).thenReturn(bitmapHeight);
|
||||||
testMinSurfaceHelper(100, 2000);
|
|
||||||
testMinSurfaceHelper(200, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testMinSurfaceHelper(int bitmapWidth, int 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);
|
clearInvocations(mSurfaceHolder);
|
||||||
setBitmapDimensions(bitmapWidth, bitmapHeight);
|
setBitmapDimensions(bitmapWidth, bitmapHeight);
|
||||||
|
|
||||||
ImageWallpaper imageWallpaper = createImageWallpaperCanvas();
|
ImageWallpaper imageWallpaper = createImageWallpaper();
|
||||||
ImageWallpaper.CanvasEngine engine =
|
ImageWallpaper.CanvasEngine engine =
|
||||||
(ImageWallpaper.CanvasEngine) imageWallpaper.onCreateEngine();
|
(ImageWallpaper.CanvasEngine) imageWallpaper.onCreateEngine();
|
||||||
engine.onCreate(mSurfaceHolder);
|
engine.onCreate(mSurfaceHolder);
|
||||||
|
|
||||||
verify(mSurfaceHolder, times(1)).setFixedSize(
|
verify(mSurfaceHolder, times(1)).setFixedSize(
|
||||||
intThat(greaterThanOrEqualTo(ImageWallpaper.CanvasEngine.MIN_SURFACE_WIDTH)),
|
intThat(equalTo(expectedSurfaceWidth)),
|
||||||
intThat(greaterThanOrEqualTo(ImageWallpaper.CanvasEngine.MIN_SURFACE_HEIGHT)));
|
intThat(equalTo(expectedSurfaceHeight)));
|
||||||
}
|
|
||||||
|
|
||||||
@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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.systemui.wallpapers.canvas;
|
package com.android.systemui.wallpapers;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
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