Fix layer damage and clipping for Text shadows

Fixes: 27787426

Change-Id: I4c65cca0cfcd343a9cfbaedd3a32b83f90df2ecf
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index ea4f4eb..f43bf86 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -195,7 +195,7 @@
 }
 
 static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRenderer,
-        const TextOp& op, const BakedOpState& state) {
+        const TextOp& op, const BakedOpState& textOpState) {
     renderer.caches().textureState().activateTexture(0);
 
     PaintUtils::TextShadow textShadow;
@@ -216,13 +216,41 @@
 
     Glop glop;
     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
-            .setRoundRectClipState(state.roundRectClipState)
+            .setRoundRectClipState(textOpState.roundRectClipState)
             .setMeshTexturedUnitQuad(nullptr)
-            .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, state.alpha)
-            .setTransform(state.computedState.transform, TransformFlags::None)
+            .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, textOpState.alpha)
+            .setTransform(textOpState.computedState.transform, TransformFlags::None)
             .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width(), sy + texture->height()))
             .build();
-    renderer.renderGlop(state, glop);
+
+    // Compute damage bounds and clip (since may differ from those in textOpState).
+    // Bounds should be same as text op, but with dx/dy offset and radius outset
+    // applied in local space.
+    auto& transform = textOpState.computedState.transform;
+    Rect shadowBounds = op.unmappedBounds; // STROKE
+    const bool expandForStroke = op.paint->getStyle() != SkPaint::kFill_Style;
+    if (expandForStroke) {
+        shadowBounds.outset(op.paint->getStrokeWidth() * 0.5f);
+    }
+    shadowBounds.translate(textShadow.dx, textShadow.dy);
+    shadowBounds.outset(textShadow.radius, textShadow.radius);
+    transform.mapRect(shadowBounds);
+    if (CC_UNLIKELY(expandForStroke &&
+            (!transform.isPureTranslate() || op.paint->getStrokeWidth() < 1.0f))) {
+        shadowBounds.outset(0.5f);
+    }
+
+    auto clipState = textOpState.computedState.clipState;
+    if (clipState->mode != ClipMode::Rectangle
+            || !clipState->rect.contains(shadowBounds)) {
+        // need clip, so pass it and clip bounds
+        shadowBounds.doIntersect(clipState->rect);
+    } else {
+        // don't need clip, ignore
+        clipState = nullptr;
+    }
+
+    renderer.renderGlop(&shadowBounds, clipState, glop);
 }
 
 enum class TextRenderType {
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
index 704bd69..6a96634 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -163,11 +163,13 @@
         GLenum dst;
     } blend;
 
+#if !HWUI_NEW_OPS
     /**
      * Bounds of the drawing command in layer space. Only mapped into layer
      * space once GlopBuilder::build() is called.
      */
     Rect bounds; // TODO: remove for HWUI_NEW_OPS
+#endif
 
     /**
      * Additional render state to enumerate:
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 2799def..7d4f410 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -492,7 +492,9 @@
 
     mOutGlop->transform.modelView.loadTranslate(destination.left, destination.top, 0.0f);
     mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f);
+#if !HWUI_NEW_OPS
     mOutGlop->bounds = destination;
+#endif
     return *this;
 }
 
@@ -516,7 +518,9 @@
 
     mOutGlop->transform.modelView.loadTranslate(left, top, 0.0f);
     mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f);
+#if !HWUI_NEW_OPS
     mOutGlop->bounds = destination;
+#endif
     return *this;
 }
 
@@ -524,8 +528,10 @@
     TRIGGER_STAGE(kModelViewStage);
 
     mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f);
+#if !HWUI_NEW_OPS
     mOutGlop->bounds = source;
     mOutGlop->bounds.translate(offsetX, offsetY);
+#endif
     return *this;
 }
 
@@ -545,8 +551,10 @@
     }
 
     mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f);
+#if !HWUI_NEW_OPS
     mOutGlop->bounds = source;
     mOutGlop->bounds.translate(offsetX, offsetY);
+#endif
     return *this;
 }
 
@@ -643,7 +651,9 @@
 
     // Final step: populate program and map bounds into render target space
     mOutGlop->fill.program = mCaches.programCache.get(mDescription);
+#if !HWUI_NEW_OPS
     mOutGlop->transform.meshTransform().mapRect(mOutGlop->bounds);
+#endif
 }
 
 void GlopBuilder::dump(const Glop& glop) {
@@ -683,7 +693,9 @@
     ALOGD_IF(glop.roundRectClipState, "Glop RRCS %p", glop.roundRectClipState);
 
     ALOGD("Glop blend %d %d", glop.blend.src, glop.blend.dst);
+#if !HWUI_NEW_OPS
     ALOGD("Glop bounds " RECT_STRING, RECT_ARGS(glop.bounds));
+#endif
 }
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index c099427..53ea7fa 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1413,7 +1413,9 @@
     if (type == GlopRenderType::Standard && !mRenderState.stencil().isWriteEnabled()) {
         // TODO: specify more clearly when a draw should dirty the layer.
         // is writing to the stencil the only time we should ignore this?
+#if !HWUI_NEW_OPS
         dirtyLayer(glop.bounds.left, glop.bounds.top, glop.bounds.right, glop.bounds.bottom);
+#endif
         mDirty = true;
     }
 }
diff --git a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
index 654ddc6..5471486 100644
--- a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
@@ -85,7 +85,9 @@
                 << "Should see conservative offset from PathCache::computeBounds";
         Rect expectedBounds(10, 15, 20, 25);
         expectedBounds.outset(expectedOffset);
+#if !HWUI_NEW_OPS
         EXPECT_EQ(expectedBounds, glop.bounds) << "bounds outset by stroke 'offset'";
+#endif
         Matrix4 expectedModelView;
         expectedModelView.loadTranslate(10 - expectedOffset, 15 - expectedOffset, 0);
         expectedModelView.scale(10 + 2 * expectedOffset, 10 + 2 * expectedOffset, 1);
diff --git a/libs/hwui/tests/unit/GlopBuilderTests.cpp b/libs/hwui/tests/unit/GlopBuilderTests.cpp
index 454011f..95543d3 100644
--- a/libs/hwui/tests/unit/GlopBuilderTests.cpp
+++ b/libs/hwui/tests/unit/GlopBuilderTests.cpp
@@ -85,7 +85,9 @@
 }
 
 static void expectGlopEq(Glop& expectedGlop, Glop& builtGlop) {
+#if !HWUI_NEW_OPS
     EXPECT_EQ(expectedGlop.bounds, builtGlop.bounds);
+#endif
     expectBlendEq(expectedGlop.blend, builtGlop.blend);
     expectFillEq(expectedGlop.fill, builtGlop.fill);
     expectMeshEq(expectedGlop.mesh, builtGlop.mesh);
@@ -136,7 +138,9 @@
     // unit quad also should be translate by additional (0.3, 0.3) to snap to exact pixels.
     goldenGlop->transform.modelView.loadTranslate(1.3, 1.3, 0);
     goldenGlop->transform.modelView.scale(99, 99, 1);
+#if !HWUI_NEW_OPS
     goldenGlop->bounds = android::uirenderer::Rect(1.70, 1.70, 100.70, 100.70);
+#endif
     goldenGlop->transform.canvas = simpleTranslate;
     goldenGlop->fill.texture.filter = GL_NEAREST;
     expectGlopEq(*goldenGlop, glop);