Create cross window blur drawable
This introduces BackgroundBlurDrawable that does cross-window blurs The drawable should not be created manually, it should be retrieved from the ViewRootImpl Test: manual Bug: 159712515 Bug: 171916625 Change-Id: I697e93fc95ba3d0a7211b235b1c5d48a1968b939
This commit is contained in:
@@ -135,6 +135,8 @@ public final class SurfaceControl implements Parcelable {
|
||||
int blurRadius);
|
||||
private static native void nativeSetLayerStack(long transactionObj, long nativeObject,
|
||||
int layerStack);
|
||||
private static native void nativeSetBlurRegions(long transactionObj, long nativeObj,
|
||||
float[][] regions, int length);
|
||||
|
||||
private static native boolean nativeClearContentFrameStats(long nativeObject);
|
||||
private static native boolean nativeGetContentFrameStats(long nativeObject, WindowContentFrameStats outStats);
|
||||
@@ -2953,6 +2955,21 @@ public final class SurfaceControl implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify what regions should be blurred on the {@link SurfaceControl}.
|
||||
*
|
||||
* @param sc SurfaceControl.
|
||||
* @param regions List of regions that will have blurs.
|
||||
* @return itself.
|
||||
* @see BlurRegion#toFloatArray()
|
||||
* @hide
|
||||
*/
|
||||
public Transaction setBlurRegions(SurfaceControl sc, float[][] regions) {
|
||||
checkPreconditions(sc);
|
||||
nativeSetBlurRegions(mNativeObject, sc.mNativeObject, regions, regions.length);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@@ -3482,4 +3499,64 @@ public final class SurfaceControl implements Parcelable {
|
||||
public static Transaction getGlobalTransaction() {
|
||||
return sGlobalTransaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for sending blur data to SurfaceFlinger.
|
||||
* @hide
|
||||
*/
|
||||
public static final class BlurRegion {
|
||||
public int blurRadius;
|
||||
public float cornerRadiusTL;
|
||||
public float cornerRadiusTR;
|
||||
public float cornerRadiusBL;
|
||||
public float cornerRadiusBR;
|
||||
public float alpha = 1;
|
||||
public boolean visible = true;
|
||||
public final Rect rect = new Rect();
|
||||
|
||||
private final float[] mFloatArray = new float[10];
|
||||
|
||||
public BlurRegion() {
|
||||
}
|
||||
|
||||
public BlurRegion(BlurRegion other) {
|
||||
rect.set(other.rect);
|
||||
blurRadius = other.blurRadius;
|
||||
alpha = other.alpha;
|
||||
cornerRadiusTL = other.cornerRadiusTL;
|
||||
cornerRadiusTR = other.cornerRadiusTR;
|
||||
cornerRadiusBL = other.cornerRadiusBL;
|
||||
cornerRadiusBR = other.cornerRadiusBR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this class into a float array that's more JNI friendly.
|
||||
*/
|
||||
public float[] toFloatArray() {
|
||||
mFloatArray[0] = blurRadius;
|
||||
mFloatArray[1] = alpha;
|
||||
mFloatArray[2] = rect.left;
|
||||
mFloatArray[3] = rect.top;
|
||||
mFloatArray[4] = rect.right;
|
||||
mFloatArray[5] = rect.bottom;
|
||||
mFloatArray[6] = cornerRadiusTL;
|
||||
mFloatArray[7] = cornerRadiusTR;
|
||||
mFloatArray[8] = cornerRadiusBL;
|
||||
mFloatArray[9] = cornerRadiusBR;
|
||||
return mFloatArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BlurRegion{"
|
||||
+ "blurRadius=" + blurRadius
|
||||
+ ", corners={" + cornerRadiusTL
|
||||
+ "," + cornerRadiusTR
|
||||
+ "," + cornerRadiusBL
|
||||
+ "," + cornerRadiusBR
|
||||
+ "}, alpha=" + alpha
|
||||
+ ", rect=" + rect
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,6 +187,7 @@ import android.window.ClientWindowFrames;
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
|
||||
import com.android.internal.inputmethod.InputMethodDebug;
|
||||
import com.android.internal.os.IResultReceiver;
|
||||
import com.android.internal.os.SomeArgs;
|
||||
@@ -654,6 +655,9 @@ public final class ViewRootImpl implements ViewParent,
|
||||
|
||||
private ScrollCaptureConnection mScrollCaptureConnection;
|
||||
|
||||
private final BackgroundBlurDrawable.Aggregator mBlurRegionAggregator =
|
||||
new BackgroundBlurDrawable.Aggregator(this);
|
||||
|
||||
/**
|
||||
* @return {@link ImeFocusController} for this instance.
|
||||
*/
|
||||
@@ -3819,6 +3823,8 @@ public final class ViewRootImpl implements ViewParent,
|
||||
private HardwareRenderer.FrameCompleteCallback createFrameCompleteCallback(Handler handler,
|
||||
boolean reportNextDraw, ArrayList<Runnable> commitCallbacks) {
|
||||
return frameNr -> {
|
||||
mBlurRegionAggregator.dispatchBlurTransactionIfNeeded(frameNr);
|
||||
|
||||
// Use a new transaction here since mRtBLASTSyncTransaction can only be accessed by
|
||||
// the render thread and mSurfaceChangedTransaction can only be accessed by the UI
|
||||
// thread. The temporary transaction is used so mRtBLASTSyncTransaction can be merged
|
||||
@@ -3849,7 +3855,8 @@ public final class ViewRootImpl implements ViewParent,
|
||||
.captureFrameCommitCallbacks();
|
||||
final boolean needFrameCompleteCallback =
|
||||
mNextDrawUseBLASTSyncTransaction || mReportNextDraw
|
||||
|| (commitCallbacks != null && commitCallbacks.size() > 0);
|
||||
|| (commitCallbacks != null && commitCallbacks.size() > 0)
|
||||
|| mBlurRegionAggregator.hasRegions();
|
||||
if (needFrameCompleteCallback) {
|
||||
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(
|
||||
createFrameCompleteCallback(mAttachInfo.mHandler, mReportNextDraw,
|
||||
@@ -9915,6 +9922,36 @@ public final class ViewRootImpl implements ViewParent,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a list of blur regions to SurfaceFlinger, tagged with a frame.
|
||||
*
|
||||
* @param regionCopy List of regions
|
||||
* @param frameNumber Frame where it should be applied (or current when using BLAST)
|
||||
*/
|
||||
public void dispatchBlurRegions(float[][] regionCopy, long frameNumber) {
|
||||
final SurfaceControl surfaceControl = getSurfaceControl();
|
||||
if (!surfaceControl.isValid()) {
|
||||
return;
|
||||
}
|
||||
if (useBLAST()) {
|
||||
synchronized (getBlastTransactionLock()) {
|
||||
getBLASTSyncTransaction().setBlurRegions(surfaceControl, regionCopy);
|
||||
}
|
||||
} else {
|
||||
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
|
||||
transaction.setBlurRegions(surfaceControl, regionCopy);
|
||||
transaction.deferTransactionUntil(surfaceControl, getSurfaceControl(), frameNumber);
|
||||
transaction.apply();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a background blur drawable for the backing {@link Surface}.
|
||||
*/
|
||||
public BackgroundBlurDrawable createBackgroundBlurDrawable() {
|
||||
return mBlurRegionAggregator.createBackgroundBlurDrawable(mContext);
|
||||
}
|
||||
|
||||
SurfaceControl.Transaction getBLASTSyncTransaction() {
|
||||
return mRtBLASTSyncTransaction;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* 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.internal.graphics.drawable;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RenderNode;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.ViewRootImpl;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
/**
|
||||
* A drawable that keeps track of a blur region, pokes a hole under it, and propagates its state
|
||||
* to SurfaceFlinger.
|
||||
*/
|
||||
public final class BackgroundBlurDrawable extends Drawable {
|
||||
|
||||
private static final String TAG = BackgroundBlurDrawable.class.getSimpleName();
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
private final Aggregator mAggregator;
|
||||
private final RenderNode mRenderNode;
|
||||
private final Paint mPaint = new Paint();
|
||||
private final Path mRectPath = new Path();
|
||||
private final float[] mTmpRadii = new float[8];
|
||||
private final SurfaceControl.BlurRegion mBlurRegion = new SurfaceControl.BlurRegion();
|
||||
|
||||
// This will be called from a thread pool.
|
||||
private final RenderNode.PositionUpdateListener mPositionUpdateListener =
|
||||
new RenderNode.PositionUpdateListener() {
|
||||
@Override
|
||||
public void positionChanged(long frameNumber, int left, int top, int right,
|
||||
int bottom) {
|
||||
synchronized (mAggregator) {
|
||||
mBlurRegion.rect.set(left, top, right, bottom);
|
||||
mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void positionLost(long frameNumber) {
|
||||
synchronized (mAggregator) {
|
||||
mBlurRegion.rect.setEmpty();
|
||||
mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private BackgroundBlurDrawable(Aggregator aggregator) {
|
||||
mAggregator = aggregator;
|
||||
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
|
||||
mRenderNode = new RenderNode("BackgroundBlurDrawable");
|
||||
mRenderNode.addPositionUpdateListener(mPositionUpdateListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(@NonNull Canvas canvas) {
|
||||
if (mRectPath.isEmpty() || !isVisible() || getAlpha() == 0) {
|
||||
return;
|
||||
}
|
||||
canvas.drawPath(mRectPath, mPaint);
|
||||
canvas.drawRenderNode(mRenderNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setVisible(boolean visible, boolean restart) {
|
||||
boolean changed = super.setVisible(visible, restart);
|
||||
if (changed) {
|
||||
mBlurRegion.visible = visible;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
mBlurRegion.alpha = alpha / 255f;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* Blur radius in pixels.
|
||||
*/
|
||||
public void setBlurRadius(int blurRadius) {
|
||||
mBlurRegion.blurRadius = blurRadius;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the corner radius, in degrees.
|
||||
*/
|
||||
public void setCornerRadius(float cornerRadius) {
|
||||
setCornerRadius(cornerRadius, cornerRadius, cornerRadius, cornerRadius);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the corner radius in degrees.
|
||||
* @param cornerRadiusTL top left radius.
|
||||
* @param cornerRadiusTR top right radius.
|
||||
* @param cornerRadiusBL bottom left radius.
|
||||
* @param cornerRadiusBR bottom right radius.
|
||||
*/
|
||||
public void setCornerRadius(float cornerRadiusTL, float cornerRadiusTR, float cornerRadiusBL,
|
||||
float cornerRadiusBR) {
|
||||
synchronized (mAggregator) {
|
||||
mBlurRegion.cornerRadiusTL = cornerRadiusTL;
|
||||
mBlurRegion.cornerRadiusTR = cornerRadiusTR;
|
||||
mBlurRegion.cornerRadiusBL = cornerRadiusBL;
|
||||
mBlurRegion.cornerRadiusBR = cornerRadiusBR;
|
||||
}
|
||||
updatePath();
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBounds(int left, int top, int right, int bottom) {
|
||||
super.setBounds(left, top, right, bottom);
|
||||
mRenderNode.setPosition(left, top, right, bottom);
|
||||
updatePath();
|
||||
}
|
||||
|
||||
private void updatePath() {
|
||||
synchronized (mAggregator) {
|
||||
mTmpRadii[0] = mTmpRadii[1] = mBlurRegion.cornerRadiusTL;
|
||||
mTmpRadii[2] = mTmpRadii[3] = mBlurRegion.cornerRadiusTR;
|
||||
mTmpRadii[4] = mTmpRadii[5] = mBlurRegion.cornerRadiusBL;
|
||||
mTmpRadii[6] = mTmpRadii[7] = mBlurRegion.cornerRadiusBR;
|
||||
}
|
||||
mRectPath.reset();
|
||||
if (getAlpha() == 0 || !isVisible()) {
|
||||
return;
|
||||
}
|
||||
Rect bounds = getBounds();
|
||||
mRectPath.addRoundRect(bounds.left, bounds.top, bounds.right, bounds.bottom, mTmpRadii,
|
||||
Path.Direction.CW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(@Nullable ColorFilter colorFilter) {
|
||||
throw new IllegalArgumentException("not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return PixelFormat.TRANSLUCENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for keeping track of all blur regions of a {@link ViewRootImpl} and posting a
|
||||
* message when it's time to propagate them.
|
||||
*/
|
||||
public static final class Aggregator {
|
||||
|
||||
private final ArrayMap<BackgroundBlurDrawable, SurfaceControl.BlurRegion> mBlurRegions =
|
||||
new ArrayMap<>();
|
||||
private final ViewRootImpl mViewRoot;
|
||||
private float[][] mTmpBlurRegionsArray;
|
||||
private boolean mNeedsUpdate;
|
||||
|
||||
public Aggregator(ViewRootImpl viewRoot) {
|
||||
mViewRoot = viewRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a blur region with default radius.
|
||||
*/
|
||||
public BackgroundBlurDrawable createBackgroundBlurDrawable(Context context) {
|
||||
BackgroundBlurDrawable drawable = new BackgroundBlurDrawable(this);
|
||||
drawable.setBlurRadius(context.getResources().getDimensionPixelSize(
|
||||
R.dimen.default_background_blur_radius));
|
||||
return drawable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from RenderThread only, already locked.
|
||||
* @param drawable
|
||||
* @param blurRegion
|
||||
*/
|
||||
void onBlurRegionUpdated(BackgroundBlurDrawable drawable,
|
||||
SurfaceControl.BlurRegion blurRegion) {
|
||||
if (blurRegion.rect.isEmpty() || blurRegion.alpha == 0 || blurRegion.blurRadius == 0
|
||||
|| !blurRegion.visible) {
|
||||
mBlurRegions.remove(drawable);
|
||||
mNeedsUpdate = true;
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Remove " + blurRegion);
|
||||
}
|
||||
} else {
|
||||
mBlurRegions.put(drawable, blurRegion);
|
||||
mNeedsUpdate = true;
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Update " + blurRegion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If there are any blur regions visible on the screen at the moment.
|
||||
*/
|
||||
public boolean hasRegions() {
|
||||
return mBlurRegions.size() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch blur updates, if there were any.
|
||||
* @param frameNumber Frame where the update should happen.
|
||||
*/
|
||||
public void dispatchBlurTransactionIfNeeded(long frameNumber) {
|
||||
synchronized (this) {
|
||||
if (!mNeedsUpdate) {
|
||||
return;
|
||||
}
|
||||
mNeedsUpdate = false;
|
||||
|
||||
if (mTmpBlurRegionsArray == null
|
||||
|| mTmpBlurRegionsArray.length != mBlurRegions.size()) {
|
||||
mTmpBlurRegionsArray = new float[mBlurRegions.size()][];
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onBlurRegionUpdated will dispatch " + mTmpBlurRegionsArray.length
|
||||
+ " regions for frame " + frameNumber);
|
||||
}
|
||||
for (int i = 0; i < mTmpBlurRegionsArray.length; i++) {
|
||||
mTmpBlurRegionsArray[i] = mBlurRegions.valueAt(i).toFloatArray();
|
||||
}
|
||||
|
||||
mViewRoot.dispatchBlurRegions(mTmpBlurRegionsArray, frameNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <private/gui/ComposerService.h>
|
||||
#include <stdio.h>
|
||||
#include <system/graphics.h>
|
||||
#include <ui/BlurRegion.h>
|
||||
#include <ui/ConfigStoreTypes.h>
|
||||
#include <ui/DeviceProductInfo.h>
|
||||
#include <ui/DisplayConfig.h>
|
||||
@@ -522,6 +523,43 @@ static void nativeSetGeometry(JNIEnv* env, jclass clazz, jlong transactionObj, j
|
||||
transaction->setGeometry(ctrl, source, dst, orientation);
|
||||
}
|
||||
|
||||
static void nativeSetBlurRegions(JNIEnv* env, jclass clazz, jlong transactionObj,
|
||||
jlong nativeObject, jobjectArray regions, jint regionsLength) {
|
||||
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
|
||||
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
|
||||
|
||||
std::vector<BlurRegion> blurRegionVector;
|
||||
const int size = regionsLength;
|
||||
float region[10];
|
||||
for (int i = 0; i < size; i++) {
|
||||
jfloatArray regionArray = (jfloatArray)env->GetObjectArrayElement(regions, i);
|
||||
env->GetFloatArrayRegion(regionArray, 0, 10, region);
|
||||
float blurRadius = region[0];
|
||||
float alpha = region[1];
|
||||
float left = region[2];
|
||||
float top = region[3];
|
||||
float right = region[4];
|
||||
float bottom = region[5];
|
||||
float cornerRadiusTL = region[6];
|
||||
float cornerRadiusTR = region[7];
|
||||
float cornerRadiusBL = region[8];
|
||||
float cornerRadiusBR = region[9];
|
||||
|
||||
blurRegionVector.push_back(BlurRegion{.blurRadius = static_cast<uint32_t>(blurRadius),
|
||||
.cornerRadiusTL = cornerRadiusTL,
|
||||
.cornerRadiusTR = cornerRadiusTR,
|
||||
.cornerRadiusBL = cornerRadiusBL,
|
||||
.cornerRadiusBR = cornerRadiusBR,
|
||||
.alpha = alpha,
|
||||
.left = static_cast<int>(left),
|
||||
.top = static_cast<int>(top),
|
||||
.right = static_cast<int>(right),
|
||||
.bottom = static_cast<int>(bottom)});
|
||||
}
|
||||
|
||||
transaction->setBlurRegions(ctrl, blurRegionVector);
|
||||
}
|
||||
|
||||
static void nativeSetSize(JNIEnv* env, jclass clazz, jlong transactionObj,
|
||||
jlong nativeObject, jint w, jint h) {
|
||||
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
|
||||
@@ -1626,6 +1664,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
|
||||
(void*)nativeSetBackgroundBlurRadius },
|
||||
{"nativeSetLayerStack", "(JJI)V",
|
||||
(void*)nativeSetLayerStack },
|
||||
{"nativeSetBlurRegions", "(JJ[[FI)V",
|
||||
(void*)nativeSetBlurRegions },
|
||||
{"nativeSetShadowRadius", "(JJF)V",
|
||||
(void*)nativeSetShadowRadius },
|
||||
{"nativeSetFrameRate", "(JJFI)V",
|
||||
|
||||
@@ -858,6 +858,7 @@
|
||||
<dimen name="waterfall_display_right_edge_size">0px</dimen>
|
||||
<dimen name="waterfall_display_bottom_edge_size">0px</dimen>
|
||||
|
||||
<dimen name="default_background_blur_radius">100dp</dimen>
|
||||
<!-- The maximum height of a thumbnail in a ThumbnailTemplate. The image will be reduced to that height in case they are bigger. -->
|
||||
<dimen name="controls_thumbnail_image_max_height">140dp</dimen>
|
||||
<!-- The maximum width of a thumbnail in a ThumbnailTemplate. The image will be reduced to that width in case they are bigger.-->
|
||||
|
||||
@@ -4078,6 +4078,7 @@
|
||||
<java-symbol type="integer" name="config_defaultBinderHeavyHitterAutoSamplerBatchSize" />
|
||||
<java-symbol type="dimen" name="config_defaultBinderHeavyHitterAutoSamplerThreshold" />
|
||||
|
||||
<java-symbol type="dimen" name="default_background_blur_radius" />
|
||||
<java-symbol type="array" name="config_keep_warming_services" />
|
||||
<java-symbol type="string" name="config_display_features" />
|
||||
<java-symbol type="array" name="config_internalFoldedPhysicalDisplayIds" />
|
||||
|
||||
Reference in New Issue
Block a user