finish up 2pt conicals

In the end it turned out best to let the subclasses
modify the mask, rather than return how to do it.
This gave more flexibility about how to calcualte it.

Add negate(x), norm(x,y).

Change-Id: Ie17050037f0441becf06897fbe31587d6709009d
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/267456
Auto-Submit: Mike Klein <mtklein@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Mike Klein <mtklein@google.com>
diff --git a/src/shaders/gradients/SkTwoPointConicalGradient.cpp b/src/shaders/gradients/SkTwoPointConicalGradient.cpp
index b01291d..efc6e74 100644
--- a/src/shaders/gradients/SkTwoPointConicalGradient.cpp
+++ b/src/shaders/gradients/SkTwoPointConicalGradient.cpp
@@ -234,32 +234,55 @@
     }
 }
 
-SkGradientShaderBase::MaskNeeded
-SkTwoPointConicalGradient::transformT(skvm::Builder* p, skvm::Uniforms* uniforms,
-                                      skvm::F32 x, skvm::F32 y, skvm::F32* t) const {
+skvm::F32 SkTwoPointConicalGradient::transformT(skvm::Builder* p, skvm::Uniforms* uniforms,
+                                                skvm::F32 x, skvm::F32 y, skvm::I32* mask) const {
     // See https://skia.org/dev/design/conical, and onAppendStages() above.
+    // There's a lot going on here, and I'm not really sure what's independent
+    // or disjoint, what can be reordered, simplified, etc.  Tweak carefully.
 
     if (fType == Type::kRadial) {
-        // As if ordinary radial for [0,r2].
-        skvm::F32 r = p->sqrt(p->mad(x,x, p->mul(y,y)));
-
-        // Rescale to [r1,r2]
         float denom = 1.0f / (fRadius2 - fRadius1),
               scale = SkTMax(fRadius1, fRadius2) * denom,
                bias =                  -fRadius1 * denom;
-        *t = p->mad(r, p->uniformF(uniforms->pushF(scale))
-                     , p->uniformF(uniforms->pushF(bias )));
-        return MaskNeeded::None;
+        return p->mad(p->norm(x,y), p->uniformF(uniforms->pushF(scale))
+                                  , p->uniformF(uniforms->pushF(bias )));
     }
 
     if (fType == Type::kStrip) {
         float r = fRadius1 / this->getCenterX1();
-        *t = p->add(x, p->sqrt(p->sub(p->splat(r*r),
-                                      p->mul(y,y))));
-        return MaskNeeded::NaNs;
+        skvm::F32 t = p->add(x, p->sqrt(p->sub(p->splat(r*r),
+                                        p->mul(y,y))));
+
+        *mask = p->eq(t,t);   // t != NaN
+        return t;
     }
 
-    return MaskNeeded::NotYetImplemented;
+    const skvm::F32 invR1 = p->uniformF(uniforms->pushF(1 / fFocalData.fR1));
+
+    skvm::F32 t;
+    if (fFocalData.isFocalOnCircle()) {
+        t = p->mad(p->div(y,x),y,x);       // (x^2 + y^2) / x  ~~>  x + y^2/x  ~~>  y/x * y + x
+    } else if (fFocalData.isWellBehaved()) {
+        t = p->sub(p->norm(x,y), p->mul(x, invR1));
+    } else {
+        skvm::F32 k = p->sqrt(p->sub(p->mul(x,x),
+                                     p->mul(y,y)));
+        if (fFocalData.isSwapped() || 1 - fFocalData.fFocalX < 0) {
+            k = p->negate(k);
+        }
+        t = p->sub(k, p->mul(x, invR1));
+    }
+
+    if (!fFocalData.isWellBehaved()) {
+        // TODO: not sure why we consider t == 0 degenerate
+        *mask = p->gt(t, p->splat(0.0f));  // t > 0 and implicitly, t != NaN
+    }
+
+    const skvm::F32 focalX = p->uniformF(uniforms->pushF(fFocalData.fFocalX));
+    if (1 - fFocalData.fFocalX < 0)    { t = p->negate(t); }
+    if (!fFocalData.isNativelyFocal()) { t = p->add(t, focalX); }
+    if (fFocalData.isSwapped())        { t = p->sub(p->splat(1.0f), t); }
+    return t;
 }
 
 /////////////////////////////////////////////////////////////////////