Fix clip area behavior for REPLACE op

bug:22291400

Work around clippath-clipped-to-view-bounds behavior by
implementing REPLACE op fast path, avoiding the transition to
SkRegion/SkPath.

Change-Id: I63c1cf961db2d6e3759d83914de1036c9ac7497a
diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp
index eb520b4..b1a6844 100644
--- a/libs/hwui/ClipArea.cpp
+++ b/libs/hwui/ClipArea.cpp
@@ -263,10 +263,11 @@
 bool ClipArea::rectangleModeClipRectWithTransform(const Rect& r,
         const mat4* transform, SkRegion::Op op) {
 
-    // TODO: we should be able to handle kReplace_Op efficiently without
-    // going through RegionMode and later falling back into RectangleMode.
-
-    if (op != SkRegion::kIntersect_Op) {
+    if (op == SkRegion::kReplace_Op && transform->rectToRect()) {
+        mClipRect = r;
+        transform->mapRect(mClipRect);
+        return true;
+    } else if (op != SkRegion::kIntersect_Op) {
         enterRegionMode();
         return regionModeClipRectWithTransform(r, transform, op);
     }
diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h
index e284af0..51ef27b 100644
--- a/libs/hwui/ClipArea.h
+++ b/libs/hwui/ClipArea.h
@@ -153,6 +153,8 @@
     }
 
     void regionFromPath(const SkPath& path, SkRegion& pathAsRegion) {
+        // TODO: this should not mask every path to the viewport - this makes it impossible to use
+        // paths to clip to larger areas (which is valid e.g. with SkRegion::kReplace_Op)
         pathAsRegion.setPath(path, createViewportRegion());
     }
 
diff --git a/libs/hwui/unit_tests/ClipAreaTests.cpp b/libs/hwui/unit_tests/ClipAreaTests.cpp
index 166d5b6..0c5e5e7 100644
--- a/libs/hwui/unit_tests/ClipAreaTests.cpp
+++ b/libs/hwui/unit_tests/ClipAreaTests.cpp
@@ -112,5 +112,16 @@
     regionBounds.set(skRect);
     EXPECT_EQ(expected, regionBounds);
 }
+
+TEST(ClipArea, replaceNegative) {
+    ClipArea area(createClipArea());
+    area.setClip(0, 0, 100, 100);
+
+    Matrix4 transform;
+    transform.loadIdentity();
+    Rect expected(-50, -50, 50, 50);
+    area.clipRectWithTransform(expected, &transform, SkRegion::kReplace_Op);
+    EXPECT_EQ(expected, area.getClipRect());
+}
 }
 }