```Some more tweaks to perspective path clipping

Bug: skia:9546
Change-Id: Ib91880e09ae6373bc133d684aa7510c67681ec1e
```
```diff --git a/samplecode/SampleClip.cpp b/samplecode/SampleClip.cpp
index 0e2546e..37cb7ec 100644
--- a/samplecode/SampleClip.cpp
+++ b/samplecode/SampleClip.cpp
```
```@@ -146,15 +146,30 @@
}

bool twoPts(SkPoint pts[2]) const {
-        if (fB) {
-            pts[0] = { 0, -fC / fB };
-            pts[1] = { 1, (-fC - fA) / fB };
-        } else if (fA) {
-            pts[0] = { -fC / fA,        0 };
-            pts[1] = { (-fC - fB) / fA, 1 };
+        // normalize plane to help with the perpendicular step, below
+        SkScalar len = SkScalarSqrt(fA*fA + fB*fB);
+        if (!len) {
+            return false;
+        }
+        SkScalar denom = SkScalarInvert(len);
+        SkScalar a = fA * denom;
+        SkScalar b = fB * denom;
+        SkScalar c = fC * denom;
+
+        // We compute p0 on the half-plane by setting one of the components to 0
+        // We compute p1 by stepping from p0 along a perpendicular to the normal
+        if (b) {
+            pts[0] = { 0, -c / b };
+            pts[1] = { b, pts[0].fY - a};
+        } else if (a) {
+            pts[0] = { -c / a,        0 };
+            pts[1] = { pts[0].fX + b, -a };
} else {
return false;
}
+
+        SkASSERT(SkScalarNearlyZero(this->operator()(pts[0].fX, pts[0].fY)));
+        SkASSERT(SkScalarNearlyZero(this->operator()(pts[1].fX, pts[1].fY)));
return true;
}
};
@@ -222,6 +237,39 @@
}

static SkPath clip(const SkPath& path, const SkHalfPlane& plane) {
+    // do a quick bounds check to see if we need to clip at all
+    const SkRect& bounds = path.getBounds();
+
+    // check whether the diagonal aligned with the normal crosses the plane
+    SkPoint diagMin, diagMax;
+    if (plane.fA >= 0) {
+        diagMin.fX = bounds.fLeft;
+        diagMax.fX = bounds.fRight;
+    } else {
+        diagMin.fX = bounds.fRight;
+        diagMax.fX = bounds.fLeft;
+    }
+    if (plane.fB >= 0) {
+        diagMin.fY = bounds.fTop;
+        diagMax.fY = bounds.fBottom;
+    } else {
+        diagMin.fY = bounds.fBottom;
+        diagMax.fY = bounds.fTop;
+    }
+    SkScalar test = plane(diagMin.fX, diagMin.fY);
+    SkScalar sign = test*plane(diagMax.fX, diagMin.fY);
+    if (sign > 0) {
+        // the path is either all on one side of the half-plane or the other
+        if (test < 0) {
+            // completely culled
+            return SkPath();
+        } else {
+            // no clipping necessary
+            return path;
+        }
+    }
+
+    // quick check failed, we have to clip
SkPoint pts[2];
if (plane.twoPts(pts)) {
return clip(path, pts[0], pts[1]);
@@ -393,7 +441,7 @@

a = 2*a/W - m;  b = 2*b/W - n;  d = 2*d/W - p;
e = 2*e/H - m;  f = 2*f/H - n;  h = 2*h/H - p;
-    i = 2*i   - m;  j = 2*j   - n;  l = 2*l   - p;
+//    i = 2*i   - m;  j = 2*j   - n;  l = 2*l   - p;

planes[0] = { m - a, n - b, p - d }; // w - x
planes[1] = { m + a, n + b, p + d }; // w + x
@@ -403,6 +451,19 @@
planes[5] = { m + i, n + j, p + l }; // w + z
}

+static SkHalfPlane half_plane_w0(const SkMatrix44& m44, SkScalar W, SkScalar H) {
+    float mx[16];
+    m44.asColMajorf(mx);
+
+    SkScalar
+//            a = mx[0], b = mx[4], /* c = mx[ 8], */ d = mx[12],
+//             e = mx[1], f = mx[5], /* g = mx[ 9], */ h = mx[13],
+//             i = mx[2], j = mx[6], /* k = mx[10], */ l = mx[14],
+             m = mx[3], n = mx[7], /* o = mx[11], */ p = mx[15];
+
+    return { m, n, p - 0.05f };  // w = 0.05f
+}
+
class HalfPlaneView3 : public Sample {
float   fNear = 0.05f;
float   fFar = 4;
@@ -413,6 +474,7 @@
SkPoint3    fUp  { 0, 1, 0 };

SkMatrix44  fRot;
+    SkPoint3    fTrans;

SkPath fPath;
@@ -440,13 +502,15 @@
SkMatrix44 get44() const {
SkMatrix44  camera,
perspective,
+                    translate,
viewport;

Sk3Perspective(&perspective, fNear, fFar, fAngle);
Sk3LookAt(&camera, fEye, fCOA, fUp);
+        translate.setTranslate(fTrans.fX, fTrans.fY, fTrans.fZ);
viewport.setScale(200, 200, 1).postTranslate( 200,  200, 0);

-        return viewport * perspective * camera * fRot * inv(viewport);
+        return viewport * perspective * camera * translate * fRot * inv(viewport);
}

void onDrawContent(SkCanvas* canvas) override {
@@ -468,8 +532,9 @@

SkHalfPlane planes[6];
half_planes(mx44, 400, 400, planes);
+        SkHalfPlane hpw = half_plane_w0(mx44, 400, 400);

-        SkPath path = clip(fPath, planes[4]);
+        SkPath path = clip(fPath, hpw);//planes[4]);
canvas->save();
canvas->concat(mx);
canvas->drawPath(path, paint);
@@ -479,7 +544,8 @@
//            draw_halfplane(canvas, p, SK_ColorRED);
//        }
draw_halfplane(canvas, planes[4], SK_ColorBLUE);
-        draw_halfplane(canvas, planes[5], SK_ColorGREEN);
+//        draw_halfplane(canvas, planes[5], SK_ColorGREEN);
+        draw_halfplane(canvas, hpw, SK_ColorRED);
}

bool onChar(SkUnichar uni) override {
@@ -492,8 +558,8 @@
case '-': this->rotate(0, 0,  delta); return true;
case '+': this->rotate(0, 0, -delta); return true;

-            case 'i': fEye.fZ += 0.1f; SkDebugf("ez %g\n", fEye.fZ); return true;
-            case 'k': fEye.fZ -= 0.1f; SkDebugf("ez %g\n", fEye.fZ); return true;
+            case 'i': fTrans.fZ += 0.1f; SkDebugf("ez %g\n", fTrans.fZ); return true;
+            case 'k': fTrans.fZ -= 0.1f; SkDebugf("ez %g\n", fTrans.fZ); return true;

case 'n': fNear += 0.1f; SkDebugf("near %g\n", fNear); return true;
case 'N': fNear -= 0.1f; SkDebugf("near %g\n", fNear); return true;
```