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
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 9b9af6f..c75b21f 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -464,10 +464,12 @@
         "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",
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index ca5f853..894b479 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -72,6 +72,10 @@
         return mImpl && !(mImpl->isEmpty());
     }
 
+    [[nodiscard]] bool hasHolePunches() const {
+        return mImpl && mImpl->hasHolePunches();
+    }
+
     [[nodiscard]] bool containsProjectionReceiver() const {
         return mImpl && mImpl->containsProjectionReceiver();
     }
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index f5b2675..e9eae3d 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -255,15 +255,19 @@
 
     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);
 
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 39ea53b..988141f 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -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 @@
 
     UsageHint mUsageHint = UsageHint::Unknown;
 
+    bool mHasHolePunches;
+
     // METHODS & FIELDS ONLY USED BY THE SKIA RENDERER
 public:
     /**
@@ -294,6 +297,8 @@
         return std::move(mAvailableDisplayList);
     }
 
+    bool hasHolePunches() { return mHasHolePunches; }
+
     /**
      * Attach unused displayList to this node for potential future reuse.
      */
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 28d2b4c..4c4a152 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -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 @@
     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)
 // ----------------------------------------------------------------------------
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 9ab2b10..e0a0be5 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -60,6 +60,8 @@
         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;
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index d1bdb71..c1feb76 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -155,6 +155,8 @@
         LOG_ALWAYS_FATAL("Not supported");
     }
 
+    virtual void punchHole(const SkRRect& rect) = 0;
+
     // ----------------------------------------------------------------------------
     // Canvas state operations
     // ----------------------------------------------------------------------------
diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp
index 89fb8bb..a611f7c 100644
--- a/libs/hwui/jni/android_graphics_Canvas.cpp
+++ b/libs/hwui/jni/android_graphics_Canvas.cpp
@@ -35,6 +35,7 @@
 #include "SkGraphics.h"
 #include "SkRegion.h"
 #include "SkVertices.h"
+#include "SkRRect.h"
 
 namespace minikin {
 class MeasuredText;
@@ -667,6 +668,11 @@
     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 @@
     {"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) {
diff --git a/libs/hwui/pipeline/skia/HolePunch.cpp b/libs/hwui/pipeline/skia/HolePunch.cpp
new file mode 100644
index 0000000..2b2bca6
--- /dev/null
+++ b/libs/hwui/pipeline/skia/HolePunch.cpp
@@ -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";
diff --git a/libs/hwui/pipeline/skia/HolePunch.h b/libs/hwui/pipeline/skia/HolePunch.h
new file mode 100644
index 0000000..92c6f77
--- /dev/null
+++ b/libs/hwui/pipeline/skia/HolePunch.h
@@ -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
\ No newline at end of file
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index cb0ff8d..5627a7e 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -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 @@
                 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);
 
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 1136e58..90e9bc6 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -163,7 +163,7 @@
 
 private:
     std::vector<Pair<VectorDrawableRoot*, SkMatrix>> mVectorDrawables;
-
+    bool mHasHolePunches;
 public:
     void appendVD(VectorDrawableRoot* r) { appendVD(r, SkMatrix::I()); }
 
@@ -171,6 +171,14 @@
         mVectorDrawables.push_back(Pair<VectorDrawableRoot*, SkMatrix>(r, mat));
     }
 
+    void setHasHolePunches(bool hasHolePunches) {
+        mHasHolePunches = hasHolePunches;
+    }
+
+    bool hasHolePunches() {
+        return mHasHolePunches;
+    }
+
     std::vector<AnimatedImageDrawable*> mAnimatedImages;
     DisplayListData mDisplayList;
 
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 61f9960..82814de 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -53,6 +53,27 @@
 
     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() {
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index ff03e0c..06f2a27 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -15,6 +15,7 @@
  */
 #pragma once
 
+#include "HolePunch.h"
 #include "RecordingCanvas.h"
 #include "ReorderBarrierDrawables.h"
 #include "SkiaCanvas.h"
@@ -43,6 +44,8 @@
         initDisplayList(renderNode, width, height);
     }
 
+    virtual void punchHole(const SkRRect& rect) override;
+
     virtual void finishRecording(uirenderer::RenderNode* destination) override;
     std::unique_ptr<SkiaDisplayList> finishRecording();
 
diff --git a/libs/hwui/pipeline/skia/TransformCanvas.cpp b/libs/hwui/pipeline/skia/TransformCanvas.cpp
new file mode 100644
index 0000000..6bfbb0d
--- /dev/null
+++ b/libs/hwui/pipeline/skia/TransformCanvas.cpp
@@ -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;
+}
diff --git a/libs/hwui/pipeline/skia/TransformCanvas.h b/libs/hwui/pipeline/skia/TransformCanvas.h
new file mode 100644
index 0000000..47f77f1
--- /dev/null
+++ b/libs/hwui/pipeline/skia/TransformCanvas.h
@@ -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;
+};