Merge "Fix merged op clipping issue"
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp
index bdc8f68..ad9559f 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/OpReorderer.cpp
@@ -91,8 +91,7 @@
 
     MergingOpBatch(batchid_t batchId, BakedOpState* op)
             : BatchBase(batchId, op, true)
-            , mClipSideFlags(op->computedState.clipSideFlags)
-            , mClipRect(op->computedState.clipRect) {
+            , mClipSideFlags(op->computedState.clipSideFlags) {
     }
 
     /*
@@ -194,22 +193,17 @@
         mBounds.unionWith(op->computedState.clippedBounds);
         mOps.push_back(op);
 
-        const int newClipSideFlags = op->computedState.clipSideFlags;
-        mClipSideFlags |= newClipSideFlags;
-
-        const Rect& opClip = op->computedState.clipRect;
-        if (newClipSideFlags & OpClipSideFlags::Left) mClipRect.left = opClip.left;
-        if (newClipSideFlags & OpClipSideFlags::Top) mClipRect.top = opClip.top;
-        if (newClipSideFlags & OpClipSideFlags::Right) mClipRect.right = opClip.right;
-        if (newClipSideFlags & OpClipSideFlags::Bottom) mClipRect.bottom = opClip.bottom;
+        // Because a new op must have passed canMergeWith(), we know it's passed the clipping compat
+        // check, and doesn't extend past a side of the clip that's in use by the merged batch.
+        // Therefore it's safe to simply always merge flags, and use the bounds as the clip rect.
+        mClipSideFlags |= op->computedState.clipSideFlags;
     }
 
     int getClipSideFlags() const { return mClipSideFlags; }
-    const Rect& getClipRect() const { return mClipRect; }
+    const Rect& getClipRect() const { return mBounds; }
 
 private:
     int mClipSideFlags;
-    Rect mClipRect;
 };
 
 OpReorderer::LayerReorderer::LayerReorderer(uint32_t width, uint32_t height,
diff --git a/libs/hwui/tests/unit/OpReordererTests.cpp b/libs/hwui/tests/unit/OpReordererTests.cpp
index 288f8af..b28e436 100644
--- a/libs/hwui/tests/unit/OpReordererTests.cpp
+++ b/libs/hwui/tests/unit/OpReordererTests.cpp
@@ -222,6 +222,46 @@
             << "Expect number of ops = 2 * loop count";
 }
 
+TEST(OpReorderer, clippedMerging) {
+    class ClippedMergingTestRenderer : public TestRendererBase {
+    public:
+        void onMergedBitmapOps(const MergedBakedOpList& opList) override {
+            EXPECT_EQ(0, mIndex);
+            mIndex += opList.count;
+            EXPECT_EQ(4u, opList.count);
+            EXPECT_EQ(Rect(10, 10, 90, 90), opList.clip);
+            EXPECT_EQ(OpClipSideFlags::Left | OpClipSideFlags::Top | OpClipSideFlags::Right,
+                    opList.clipSideFlags);
+        }
+    };
+    auto node = TestUtils::createNode(0, 0, 100, 100,
+            [](RenderProperties& props, TestCanvas& canvas) {
+        SkBitmap bitmap = TestUtils::createSkBitmap(20, 20);
+
+        // left side clipped (to inset left half)
+        canvas.clipRect(10, 0, 50, 100, SkRegion::kReplace_Op);
+        canvas.drawBitmap(bitmap, 0, 40, nullptr);
+
+        // top side clipped (to inset top half)
+        canvas.clipRect(0, 10, 100, 50, SkRegion::kReplace_Op);
+        canvas.drawBitmap(bitmap, 40, 0, nullptr);
+
+        // right side clipped (to inset right half)
+        canvas.clipRect(50, 0, 90, 100, SkRegion::kReplace_Op);
+        canvas.drawBitmap(bitmap, 80, 40, nullptr);
+
+        // bottom not clipped, just abutting (inset bottom half)
+        canvas.clipRect(0, 50, 100, 90, SkRegion::kReplace_Op);
+        canvas.drawBitmap(bitmap, 40, 70, nullptr);
+    });
+
+    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+            createSyncedNodeList(node), sLightCenter);
+    ClippedMergingTestRenderer renderer;
+    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(4, renderer.getIndex());
+}
+
 TEST(OpReorderer, textMerging) {
     class TextMergingTestRenderer : public TestRendererBase {
     public: