Holepunch layers for SurfaceView

Update RenderNodeDrawable to hole punch areas into
layers created for SurfaceView

Bug: 184297961
Test: Added CTS test to SurfaceViewTests
Change-Id: I1f03a4fe34c5a8b7411ebe728ea3d4195fcd1fac
This commit is contained in:
Nader Jawad
2021-03-29 18:51:29 -07:00
parent 4563f9dfb5
commit 2dc632aee3
19 changed files with 235 additions and 7 deletions

View File

@@ -33,7 +33,6 @@ import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.RenderNode;
@@ -670,10 +669,16 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
if (mClipSurfaceToBounds && mClipBounds != null) {
mTmpRect.intersect(mClipBounds);
}
canvas.drawRoundRect(mTmpRect.left, mTmpRect.top, mTmpRect.right, mTmpRect.bottom,
mCornerRadius, mCornerRadius, mRoundedViewportPaint);
canvas.punchHole(
mTmpRect.left,
mTmpRect.top,
mTmpRect.right,
mTmpRect.bottom,
mCornerRadius,
mCornerRadius
);
} else {
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
canvas.punchHole(0f, 0f, getWidth(), getHeight(), 0f, 0f);
}
}

View File

@@ -670,6 +670,13 @@ public abstract class BaseCanvas {
indices, indexOffset, indexCount, paint.getNativeInstance());
}
/**
* @hide
*/
public void punchHole(float left, float top, float right, float bottom, float rx, float ry) {
nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry);
}
/**
* @hide
*/
@@ -815,4 +822,7 @@ public abstract class BaseCanvas {
private static native void nDrawTextOnPath(long nativeCanvas, String text, long nativePath,
float hOffset, float vOffset, int flags, long nativePaint);
private static native void nPunchHole(long renderer, float left, float top, float right,
float bottom, float rx, float ry);
}

View File

@@ -610,6 +610,14 @@ public class BaseRecordingCanvas extends Canvas {
indices, indexOffset, indexCount, paint.getNativeInstance());
}
/**
* @hide
*/
@Override
public void punchHole(float left, float top, float right, float bottom, float rx, float ry) {
nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry);
}
@FastNative
private static native void nDrawBitmap(long nativeCanvas, long bitmapHandle, float left,
float top, long nativePaintOrZero, int canvasDensity, int screenDensity,
@@ -735,4 +743,8 @@ public class BaseRecordingCanvas extends Canvas {
@FastNative
private static native void nDrawTextOnPath(long nativeCanvas, String text, long nativePath,
float hOffset, float vOffset, int flags, long nativePaint);
@FastNative
private static native void nPunchHole(long renderer, float left, float top, float right,
float bottom, float rx, float ry);
}

View File

@@ -464,10 +464,12 @@ cc_defaults {
"canvas/CanvasOpBuffer.cpp",
"canvas/CanvasOpRasterizer.cpp",
"effects/StretchEffect.cpp",
"pipeline/skia/HolePunch.cpp",
"pipeline/skia/SkiaDisplayList.cpp",
"pipeline/skia/SkiaRecordingCanvas.cpp",
"pipeline/skia/RenderNodeDrawable.cpp",
"pipeline/skia/ReorderBarrierDrawables.cpp",
"pipeline/skia/TransformCanvas.cpp",
"renderthread/Frame.cpp",
"renderthread/RenderTask.cpp",
"renderthread/TimeLord.cpp",

View File

@@ -72,6 +72,10 @@ public:
return mImpl && !(mImpl->isEmpty());
}
[[nodiscard]] bool hasHolePunches() const {
return mImpl && mImpl->hasHolePunches();
}
[[nodiscard]] bool containsProjectionReceiver() const {
return mImpl && mImpl->containsProjectionReceiver();
}

View File

@@ -255,15 +255,19 @@ void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool fu
if (mDisplayList) {
info.out.hasFunctors |= mDisplayList.hasFunctor();
mHasHolePunches = mDisplayList.hasHolePunches();
bool isDirty = mDisplayList.prepareListAndChildren(
observer, info, childFunctorsNeedLayer,
[](RenderNode* child, TreeObserver& observer, TreeInfo& info,
bool functorsNeedLayer) {
[this](RenderNode* child, TreeObserver& observer, TreeInfo& info,
bool functorsNeedLayer) {
child->prepareTreeImpl(observer, info, functorsNeedLayer);
mHasHolePunches |= child->hasHolePunches();
});
if (isDirty) {
damageSelf(info);
}
} else {
mHasHolePunches = false;
}
pushLayerUpdate(info);

View File

@@ -35,6 +35,7 @@
#include "DisplayList.h"
#include "Matrix.h"
#include "RenderProperties.h"
#include "pipeline/skia/HolePunch.h"
#include "pipeline/skia/SkiaDisplayList.h"
#include "pipeline/skia/SkiaLayer.h"
@@ -284,6 +285,8 @@ private:
UsageHint mUsageHint = UsageHint::Unknown;
bool mHasHolePunches;
// METHODS & FIELDS ONLY USED BY THE SKIA RENDERER
public:
/**
@@ -294,6 +297,8 @@ public:
return std::move(mAvailableDisplayList);
}
bool hasHolePunches() { return mHasHolePunches; }
/**
* Attach unused displayList to this node for potential future reuse.
*/

View File

@@ -23,6 +23,7 @@
#include "hwui/MinikinUtils.h"
#include "hwui/PaintFilter.h"
#include "pipeline/skia/AnimatedDrawables.h"
#include "pipeline/skia/HolePunch.h"
#include <SkAndroidFrameworkUtils.h>
#include <SkAnimatedImage.h>
@@ -244,6 +245,13 @@ const SkiaCanvas::SaveRec* SkiaCanvas::currentSaveRec() const {
return (rec && rec->saveCount == currentSaveCount) ? rec : nullptr;
}
void SkiaCanvas::punchHole(const SkRRect& rect) {
SkPaint paint = SkPaint();
paint.setColor(0);
paint.setBlendMode(SkBlendMode::kClear);
mCanvas->drawRRect(rect, paint);
}
// ----------------------------------------------------------------------------
// functions to emulate legacy SaveFlags (i.e. independent matrix/clip flags)
// ----------------------------------------------------------------------------

View File

@@ -60,6 +60,8 @@ public:
LOG_ALWAYS_FATAL("SkiaCanvas does not support enableZ");
}
virtual void punchHole(const SkRRect& rect) override;
virtual void setBitmap(const SkBitmap& bitmap) override;
virtual bool isOpaque() override;

View File

@@ -155,6 +155,8 @@ public:
LOG_ALWAYS_FATAL("Not supported");
}
virtual void punchHole(const SkRRect& rect) = 0;
// ----------------------------------------------------------------------------
// Canvas state operations
// ----------------------------------------------------------------------------

View File

@@ -35,6 +35,7 @@
#include "SkGraphics.h"
#include "SkRegion.h"
#include "SkVertices.h"
#include "SkRRect.h"
namespace minikin {
class MeasuredText;
@@ -667,6 +668,11 @@ static void setCompatibilityVersion(JNIEnv* env, jobject, jint apiLevel) {
Canvas::setCompatibilityVersion(apiLevel);
}
static void punchHole(JNIEnv* env, jobject, jlong canvasPtr, jfloat left, jfloat top, jfloat right,
jfloat bottom, jfloat rx, jfloat ry) {
auto canvas = reinterpret_cast<Canvas*>(canvasPtr);
canvas->punchHole(SkRRect::MakeRectXY(SkRect::MakeLTRB(left, top, right, bottom), rx, ry));
}
}; // namespace CanvasJNI
@@ -740,6 +746,7 @@ static const JNINativeMethod gDrawMethods[] = {
{"nDrawTextRun","(JLjava/lang/String;IIIIFFZJ)V", (void*) CanvasJNI::drawTextRunString},
{"nDrawTextOnPath","(J[CIIJFFIJ)V", (void*) CanvasJNI::drawTextOnPathChars},
{"nDrawTextOnPath","(JLjava/lang/String;JFFIJ)V", (void*) CanvasJNI::drawTextOnPathString},
{"nPunchHole", "(JFFFFFF)V", (void*) CanvasJNI::punchHole}
};
int register_android_graphics_Canvas(JNIEnv* env) {

View File

@@ -0,0 +1,21 @@
/*
* Copyright (C) 2021 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.
*/
#include "HolePunch.h"
#include <string>
using namespace android::uirenderer::skiapipeline;
const std::string HOLE_PUNCH_ANNOTATION = "surface_hole_punch";

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2021 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.
*/
#pragma once
#include <string>
#include "SkRRect.h"
namespace android {
namespace uirenderer {
namespace skiapipeline {
const static std::string HOLE_PUNCH_ANNOTATION;
} // namespace skiapipeline
} // namespace uirenderer
} // namespace android

View File

@@ -18,6 +18,7 @@
#include <SkPaintFilterCanvas.h>
#include "RenderNode.h"
#include "SkiaDisplayList.h"
#include "TransformCanvas.h"
#include "utils/TraceUtils.h"
#include <include/effects/SkImageFilters.h>
@@ -256,6 +257,11 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
canvas->drawAnnotation(bounds, String8::format(
"SurfaceID|%" PRId64, renderNode->uniqueId()).c_str(), nullptr);
}
if (renderNode->hasHolePunches()) {
TransformCanvas transformCanvas(canvas);
displayList->draw(&transformCanvas);
}
canvas->drawImageRect(snapshotImage, bounds, bounds, sampling, &paint,
SkCanvas::kStrict_SrcRectConstraint);

View File

@@ -163,7 +163,7 @@ public:
private:
std::vector<Pair<VectorDrawableRoot*, SkMatrix>> mVectorDrawables;
bool mHasHolePunches;
public:
void appendVD(VectorDrawableRoot* r) { appendVD(r, SkMatrix::I()); }
@@ -171,6 +171,14 @@ public:
mVectorDrawables.push_back(Pair<VectorDrawableRoot*, SkMatrix>(r, mat));
}
void setHasHolePunches(bool hasHolePunches) {
mHasHolePunches = hasHolePunches;
}
bool hasHolePunches() {
return mHasHolePunches;
}
std::vector<AnimatedImageDrawable*> mAnimatedImages;
DisplayListData mDisplayList;

View File

@@ -53,6 +53,27 @@ void SkiaRecordingCanvas::initDisplayList(uirenderer::RenderNode* renderNode, in
mDisplayList->attachRecorder(&mRecorder, SkIRect::MakeWH(width, height));
SkiaCanvas::reset(&mRecorder);
mDisplayList->setHasHolePunches(false);
}
void SkiaRecordingCanvas::punchHole(const SkRRect& rect) {
// Add the marker annotation to allow HWUI to determine where the current
// clip/transformation should be applied
SkVector vector = rect.getSimpleRadii();
const int dataSize = 2;
float data[dataSize];
data[0] = vector.x();
data[1] = vector.y();
mRecorder.drawAnnotation(rect.rect(), HOLE_PUNCH_ANNOTATION.c_str(),
SkData::MakeWithCopy(data, dataSize));
// Clear the current rect within the layer itself
SkPaint paint = SkPaint();
paint.setColor(0);
paint.setBlendMode(SkBlendMode::kClear);
mRecorder.drawRRect(rect, paint);
mDisplayList->setHasHolePunches(true);
}
std::unique_ptr<SkiaDisplayList> SkiaRecordingCanvas::finishRecording() {

View File

@@ -15,6 +15,7 @@
*/
#pragma once
#include "HolePunch.h"
#include "RecordingCanvas.h"
#include "ReorderBarrierDrawables.h"
#include "SkiaCanvas.h"
@@ -43,6 +44,8 @@ public:
initDisplayList(renderNode, width, height);
}
virtual void punchHole(const SkRRect& rect) override;
virtual void finishRecording(uirenderer::RenderNode* destination) override;
std::unique_ptr<SkiaDisplayList> finishRecording();

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2021 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.
*/
#include "TransformCanvas.h"
#include "HolePunch.h"
#include "SkData.h"
#include "SkDrawable.h"
using namespace android::uirenderer::skiapipeline;
void TransformCanvas::onDrawAnnotation(const SkRect& rect, const char* key, SkData* value) {
if (HOLE_PUNCH_ANNOTATION == key) {
auto* rectParams = static_cast<const float*>(value->data());
float radiusX = rectParams[0];
float radiusY = rectParams[1];
SkRRect roundRect = SkRRect::MakeRectXY(rect, radiusX, radiusY);
SkPaint paint;
paint.setColor(0);
paint.setBlendMode(SkBlendMode::kClear);
mWrappedCanvas->drawRRect(roundRect, paint);
}
}
void TransformCanvas::onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) {
drawable->draw(this, matrix);
}
bool TransformCanvas::onFilter(SkPaint& paint) const {
return false;
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2021 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.
*/
#pragma once
#include <include/core/SkCanvas.h>
#include "SkPaintFilterCanvas.h"
class TransformCanvas : public SkPaintFilterCanvas {
public:
TransformCanvas(SkCanvas* target) : SkPaintFilterCanvas(target), mWrappedCanvas(target) {}
protected:
bool onFilter(SkPaint& paint) const override;
protected:
void onDrawAnnotation(const SkRect& rect, const char* key, SkData* value) override;
void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override;
private:
// We don't own the canvas so just maintain a raw pointer to it
SkCanvas* mWrappedCanvas;
};