Clip TouchFeedbackDrawable effect to receiver Outline

Projected RenderNodes are now wrapped with a ClipRect or masked
SaveLayer, so that they are clipped to the outline of the projection
receiver surface.

Change-Id: I1d4afc1bb5d638d650bc0b1dac51a498f216773e
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index cf21834..10c5fb8 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -76,7 +76,7 @@
  */
 void RenderNode::output(uint32_t level) {
     ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this,
-            mName.string(), isRenderable());
+            getName(), isRenderable());
     ALOGD("%*s%s %d", level * 2, "", "Save",
             SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
 
@@ -86,7 +86,7 @@
         mDisplayListData->displayListOps[i]->output(level, flags);
     }
 
-    ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, mName.string());
+    ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, getName());
 }
 
 void RenderNode::prepareTree(TreeInfo& info) {
@@ -260,12 +260,13 @@
     for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
         DrawDisplayListOp* childOp = mDisplayListData->children()[i];
         childOp->mDisplayList->computeOrderingImpl(childOp,
-                &mProjectedNodes, &mat4::identity());
+                properties().getOutline().getPath(), &mProjectedNodes, &mat4::identity());
     }
 }
 
 void RenderNode::computeOrderingImpl(
         DrawDisplayListOp* opState,
+        const SkPath* outlineOfProjectionSurface,
         Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
         const mat4* transformFromProjectionSurface) {
     mProjectedNodes.clear();
@@ -293,6 +294,7 @@
             DrawDisplayListOp* childOp = mDisplayListData->children()[i];
             RenderNode* child = childOp->mDisplayList;
 
+            const SkPath* projectionOutline = NULL;
             Vector<DrawDisplayListOp*>* projectionChildren = NULL;
             const mat4* projectionTransform = NULL;
             if (isProjectionReceiver && !child->properties().getProjectBackwards()) {
@@ -301,6 +303,7 @@
                 // Note that if a direct descendent is projecting backwards, we pass it's
                 // grandparent projection collection, since it shouldn't project onto it's
                 // parent, where it will already be drawing.
+                projectionOutline = properties().getOutline().getPath();
                 projectionChildren = &mProjectedNodes;
                 projectionTransform = &mat4::identity();
             } else {
@@ -308,10 +311,12 @@
                     applyViewPropertyTransforms(localTransformFromProjectionSurface);
                     haveAppliedPropertiesToProjection = true;
                 }
+                projectionOutline = outlineOfProjectionSurface;
                 projectionChildren = compositedChildrenOfProjectionSurface;
                 projectionTransform = &localTransformFromProjectionSurface;
             }
-            child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
+            child->computeOrderingImpl(childOp,
+                    projectionOutline, projectionChildren, projectionTransform);
         }
     }
 }
@@ -351,7 +356,7 @@
         : mReplayStruct(replayStruct), mLevel(level) {}
     inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
 #if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
-        properties().getReplayStruct().mRenderer.eventMark(operation->name());
+        mReplayStruct.mRenderer.eventMark(operation->name());
 #endif
         operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
     }
@@ -361,8 +366,6 @@
     }
     inline void endMark() {
         mReplayStruct.mRenderer.endMark();
-        DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(),
-                mReplayStruct.mDrawGlStatus);
     }
     inline int level() { return mLevel; }
     inline int replayFlags() { return mReplayStruct.mReplayFlags; }
@@ -467,6 +470,10 @@
         endIndex = size;
         shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
     }
+
+    DISPLAY_LIST_LOGD("%*s%d %s 3d children:", (handler.level() + 1) * 2, "",
+            endIndex - drawIndex, mode == kNegativeZChildren ? "negative" : "positive");
+
     float lastCasterZ = 0.0f;
     while (shadowIndex < endIndex || drawIndex < endIndex) {
         if (shadowIndex < endIndex) {
@@ -503,6 +510,42 @@
 
 template <class T>
 void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler) {
+    DISPLAY_LIST_LOGD("%*s%d projected children:", (handler.level() + 1) * 2, "", mProjectedNodes.size());
+    const SkPath* projectionReceiverOutline = properties().getOutline().getPath();
+    bool maskProjecteesWithPath = projectionReceiverOutline != NULL
+            && !projectionReceiverOutline->isRect(NULL);
+    int restoreTo = renderer.getSaveCount();
+
+    // If the projection reciever has an outline, we mask each of the projected rendernodes to it
+    // Either with clipRect, or special saveLayer masking
+    LinearAllocator& alloc = handler.allocator();
+    if (projectionReceiverOutline != NULL) {
+        const SkRect& outlineBounds = projectionReceiverOutline->getBounds();
+        if (projectionReceiverOutline->isRect(NULL)) {
+            // mask to the rect outline simply with clipRect
+            handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
+                    PROPERTY_SAVECOUNT, properties().getClipToBounds());
+            ClipRectOp* clipOp = new (alloc) ClipRectOp(
+                    outlineBounds.left(), outlineBounds.top(),
+                    outlineBounds.right(), outlineBounds.bottom(), SkRegion::kIntersect_Op);
+            handler(clipOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
+        } else {
+            // wrap the projected RenderNodes with a SaveLayer that will mask to the outline
+            SaveLayerOp* op = new (alloc) SaveLayerOp(
+                    outlineBounds.left(), outlineBounds.top(),
+                    outlineBounds.right(), outlineBounds.bottom(),
+                    255, SkCanvas::kARGB_ClipLayer_SaveFlag);
+            op->setMask(projectionReceiverOutline);
+            handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
+
+            /* TODO: add optimizations here to take advantage of placement/size of projected
+             * children (which may shrink saveLayer area significantly). This is dependent on
+             * passing actual drawing/dirtying bounds of projected content down to native.
+             */
+        }
+    }
+
+    // draw projected nodes
     for (size_t i = 0; i < mProjectedNodes.size(); i++) {
         DrawDisplayListOp* childOp = mProjectedNodes[i];
 
@@ -514,6 +557,11 @@
         childOp->mSkipInOrderDraw = true;
         renderer.restoreToCount(restoreTo);
     }
+
+    if (projectionReceiverOutline != NULL) {
+        handler(new (alloc) RestoreToCountOp(restoreTo),
+                PROPERTY_SAVECOUNT, properties().getClipToBounds());
+    }
 }
 
 /**
@@ -529,17 +577,17 @@
 void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
     const int level = handler.level();
     if (mDisplayListData->isEmpty() || properties().getAlpha() <= 0) {
-        DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string());
+        DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, getName());
         return;
     }
 
-    handler.startMark(mName.string());
+    handler.startMark(getName());
 
 #if DEBUG_DISPLAY_LIST
-    Rect* clipRect = renderer.getClipRect();
-    DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.0f, %.0f, %.0f",
-            level * 2, "", this, mName.string(), clipRect->left, clipRect->top,
-            clipRect->right, clipRect->bottom);
+    const Rect& clipRect = renderer.getLocalClipBounds();
+    DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), localClipBounds: %.0f, %.0f, %.0f, %.0f",
+            level * 2, "", this, getName(),
+            clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
 #endif
 
     LinearAllocator& alloc = handler.allocator();
@@ -587,6 +635,7 @@
             PROPERTY_SAVECOUNT, properties().getClipToBounds());
     renderer.setOverrideLayerAlpha(1.0f);
 
+    DISPLAY_LIST_LOGD("%*sDone (%p, %s)", level * 2, "", this, getName());
     handler.endMark();
 }