Use path intersection instead of saveLayer+mesh to mask projected ripples

bug:14297149

SaveLayer's performance cost is high, and proportional to the surface
being projected onto. Since ripples (even unbounded ones) are now
always projected to the arbitrary background content behind them, this
cost is especially important to avoid.

This removes the last semi-secret, saveLayer from the projected
ripple implementation.

Also fixes the HW test app to correctly demonstrate this projection
masking behavior.

Additionaly, alters PathTessellator to gracefully handle
counter-clockwise paths, and simplifies the work done by
ShadowTessellator to ensure all of its paths are counterclockwise.

Change-Id: Ibe9e12812bd10a774e20b1d444a140c368cbba8c
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index c1f61d6..e7c6c05 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -37,6 +37,7 @@
 
 #include <SkPath.h>
 #include <SkPaint.h>
+#include <SkPoint.h>
 #include <SkGeometry.h> // WARNING: Internal Skia Header
 
 #include <stdlib.h>
@@ -912,6 +913,39 @@
     Vertex::set(newVertex, x, y);
 }
 
+class ClockwiseEnforcer {
+public:
+    void addPoint(const SkPoint& point) {
+        double x = point.x();
+        double y = point.y();
+
+        if (initialized) {
+            sum += (x + lastX) * (y - lastY);
+        } else {
+            initialized = true;
+        }
+
+        lastX = x;
+        lastY = y;
+    }
+    void reverseVectorIfNotClockwise(Vector<Vertex>& vertices) {
+        if (sum < 0) {
+            // negative sum implies CounterClockwise
+            const int size = vertices.size();
+            for (int i = 0; i < size / 2; i++) {
+                Vertex tmp = vertices[i];
+                int k = size - 1 - i;
+                vertices.replaceAt(vertices[k], i);
+                vertices.replaceAt(tmp, k);
+            }
+        }
+    }
+private:
+    bool initialized = false;
+    double lastX, lastY;
+    double sum = 0;
+};
+
 bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose,
         float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
         Vector<Vertex>& outputVertices) {
@@ -922,18 +956,22 @@
     SkPath::Iter iter(path, forceClose);
     SkPoint pts[4];
     SkPath::Verb v;
+    ClockwiseEnforcer clockwiseEnforcer;
     while (SkPath::kDone_Verb != (v = iter.next(pts))) {
             switch (v) {
             case SkPath::kMove_Verb:
                 pushToVector(outputVertices, pts[0].x(), pts[0].y());
                 ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
+                clockwiseEnforcer.addPoint(pts[0]);
                 break;
             case SkPath::kClose_Verb:
                 ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
+                clockwiseEnforcer.addPoint(pts[0]);
                 break;
             case SkPath::kLine_Verb:
                 ALOGV("kLine_Verb %f %f -> %f %f", pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y());
                 pushToVector(outputVertices, pts[1].x(), pts[1].y());
+                clockwiseEnforcer.addPoint(pts[1]);
                 break;
             case SkPath::kQuad_Verb:
                 ALOGV("kQuad_Verb");
@@ -942,6 +980,8 @@
                         pts[2].x(), pts[2].y(),
                         pts[1].x(), pts[1].y(),
                         sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
+                clockwiseEnforcer.addPoint(pts[1]);
+                clockwiseEnforcer.addPoint(pts[2]);
                 break;
             case SkPath::kCubic_Verb:
                 ALOGV("kCubic_Verb");
@@ -951,6 +991,9 @@
                         pts[3].x(), pts[3].y(),
                         pts[2].x(), pts[2].y(),
                         sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
+                clockwiseEnforcer.addPoint(pts[1]);
+                clockwiseEnforcer.addPoint(pts[2]);
+                clockwiseEnforcer.addPoint(pts[3]);
                 break;
             case SkPath::kConic_Verb: {
                 ALOGV("kConic_Verb");
@@ -965,6 +1008,8 @@
                             quads[offset+1].x(), quads[offset+1].y(),
                             sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
                 }
+                clockwiseEnforcer.addPoint(pts[1]);
+                clockwiseEnforcer.addPoint(pts[2]);
                 break;
             }
             default:
@@ -972,13 +1017,17 @@
             }
     }
 
+    bool wasClosed = false;
     int size = outputVertices.size();
     if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x &&
             outputVertices[0].y == outputVertices[size - 1].y) {
         outputVertices.pop();
-        return true;
+        wasClosed = true;
     }
-    return false;
+
+    // ensure output vector is clockwise
+    clockwiseEnforcer.reverseVectorIfNotClockwise(outputVertices);
+    return wasClosed;
 }
 
 ///////////////////////////////////////////////////////////////////////////////