Update 2pt conical gradient in raster pipeline

The updated algorithm matches our new GPU algorithm
(https://skia.org/dev/design/conical) and it brings
about 7%-26% speedup. In the next CL, I'll simplify
the GPU code by reusing the CPU code in this CL.

    7.20% faster in gradient_conical_clamp_hicolor
    8.94% faster in gradient_conicalZero_clamp_hicolor
   10.00% faster in gradient_conicalOut_clamp_hicolor
   11.72% faster in gradient_conicalOutZero_clamp_hicolor
   13.62% faster in gradient_conical_clamp_3color
   16.52% faster in gradient_conicalZero_clamp_3color
   17.48% faster in gradient_conical_clamp
   17.70% faster in gradient_conical_clamp_shallow
   20.60% faster in gradient_conicalOut_clamp_3color
   20.98% faster in gradient_conicalOutZero_clamp_3color
   21.79% faster in gradient_conicalZero_clamp
   22.48% faster in gradient_conicalOut_clamp
   26.13% faster in gradient_conicalOutZero_clamp

Bug: skia:
Change-Id: Ia159495e1c77658cb28e48c9edf84938464e501c
Reviewed-on: https://skia-review.googlesource.com/90262
Commit-Queue: Yuqian Li <liyuqian@google.com>
Reviewed-by: Mike Klein <mtklein@chromium.org>
diff --git a/src/shaders/gradients/SkTwoPointConicalGradient.cpp b/src/shaders/gradients/SkTwoPointConicalGradient.cpp
index 8cf6409..7c6b334 100644
--- a/src/shaders/gradients/SkTwoPointConicalGradient.cpp
+++ b/src/shaders/gradients/SkTwoPointConicalGradient.cpp
@@ -12,6 +12,49 @@
 #include "SkWriteBuffer.h"
 #include "../../jumper/SkJumper.h"
 
+// Please see https://skia.org/dev/design/conical for how our shader works.
+
+void SkTwoPointConicalGradient::FocalData::set(SkScalar r0, SkScalar r1, SkMatrix& matrix) {
+#ifdef SK_SUPPORT_LEGACY_2PT_CONICAL
+    // Just initialize the memory. We are not supposed to do anything in legacy mode.
+    fIsSwapped = false;
+    fFocalX = fR1 = 0;
+#else
+    fIsSwapped = false;
+    fFocalX = r0 / (r0 - r1);
+    if (SkScalarNearlyZero(fFocalX - 1)) {
+        // swap r0, r1
+        matrix.postTranslate(-1, 0);
+        matrix.postScale(-1, 1);
+        std::swap(r0, r1);
+        fFocalX = 0; // because r0 is now 0
+        fIsSwapped = true;
+    }
+
+    // Map {focal point, (1, 0)} to {(0, 0), (1, 0)}
+    const SkPoint from[2]   = { {fFocalX, 0}, {1, 0} };
+    const SkPoint to[2]     = { {0, 0}, {1, 0} };
+    SkMatrix focalMatrix;
+    if (!focalMatrix.setPolyToPoly(from, to, 2)) {
+        SkDEBUGFAILF("Mapping focal point failed unexpectedly for focalX = %f.\n", fFocalX);
+        // We won't be able to draw the gradient; at least make sure that we initialize the
+        // memory to prevent security issues.
+        focalMatrix = SkMatrix::MakeScale(1, 1);
+    }
+    matrix.postConcat(focalMatrix);
+    fR1 = r1 / SkScalarAbs(1 - fFocalX); // focalMatrix has a scale of 1/(1-f)
+
+    // The following transformations are just to accelerate the shader computation by saving
+    // some arithmatic operations.
+    if (this->isFocalOnCircle()) {
+        matrix.postScale(0.5, 0.5);
+    } else {
+        matrix.postScale(fR1 / (fR1 * fR1 - 1), 1 / sqrt(SkScalarAbs(fR1 * fR1 - 1)));
+    }
+    matrix.postScale(SkScalarAbs(1 - fFocalX), SkScalarAbs(1 - fFocalX)); // scale |1 - f|
+#endif
+}
+
 sk_sp<SkShader> SkTwoPointConicalGradient::Create(const SkPoint& c0, SkScalar r0,
                                                   const SkPoint& c1, SkScalar r1,
                                                   const Descriptor& desc) {
@@ -34,18 +77,22 @@
             return nullptr;
         }
 
-        // General two-point case.
-        gradientType = Type::kTwoPoint;
+        gradientType = SkScalarNearlyZero(r1 - r0) ? Type::kStrip : Type::kFocal;
     }
 
+    FocalData focalData;
+    if (gradientType == Type::kFocal) {
+        const auto dCenter = (c0 - c1).length();
+        focalData.set(r0 / dCenter, r1 / dCenter, gradientMatrix); // this may change gradientMatrix
+    }
     return sk_sp<SkShader>(new SkTwoPointConicalGradient(c0, r0, c1, r1, desc,
-                                                         gradientType, gradientMatrix));
+                                                         gradientType, gradientMatrix, focalData));
 }
 
 SkTwoPointConicalGradient::SkTwoPointConicalGradient(
         const SkPoint& start, SkScalar startRadius,
         const SkPoint& end, SkScalar endRadius,
-        const Descriptor& desc, Type type, const SkMatrix& gradientMatrix)
+        const Descriptor& desc, Type type, const SkMatrix& gradientMatrix, const FocalData& data)
     : SkGradientShaderBase(desc, gradientMatrix)
     , fCenter1(start)
     , fCenter2(end)
@@ -55,6 +102,9 @@
 {
     // this is degenerate, and should be caught by our caller
     SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
+    if (type == Type::kFocal) {
+        fFocalData = data;
+    }
 }
 
 bool SkTwoPointConicalGradient::isOpaque() const {
@@ -189,6 +239,7 @@
         return;
     }
 
+#ifdef SK_SUPPORT_LEGACY_2PT_CONICAL
     const auto dCenter = (fCenter1 - fCenter2).length();
 
     // Since we've squashed the centers into a unit vector, we must also scale
@@ -230,7 +281,48 @@
     }
 
     if (!isWellBehaved) {
-        p->append(SkRasterPipeline::mask_2pt_conical_degenerates, ctx);
+        p->append(SkRasterPipeline::mask_2pt_conical_degenerates_legacy, ctx);
         postPipeline->append(SkRasterPipeline::apply_vector_mask, &ctx->fMask);
     }
+#else
+    if (fType == Type::kStrip) {
+        auto* ctx = alloc->make<SkJumper_2PtConicalCtx>();
+        SkScalar scaledR0 = fRadius1 / this->getCenterX1();
+        ctx->fP0 = scaledR0 * scaledR0;
+        p->append(SkRasterPipeline::xy_to_2pt_conical_strip, ctx);
+        p->append(SkRasterPipeline::mask_2pt_conical_nan, ctx);
+        postPipeline->append(SkRasterPipeline::apply_vector_mask, &ctx->fMask);
+        return;
+    }
+
+    auto* ctx = alloc->make<SkJumper_2PtConicalCtx>();
+    ctx->fP0 = 1/fFocalData.fR1;
+    ctx->fP1 = fFocalData.fFocalX;
+
+    if (fFocalData.isFocalOnCircle()) {
+        p->append(SkRasterPipeline::xy_to_2pt_conical_focal_on_circle);
+    } else if (fFocalData.isWellBehaved()) {
+        p->append(SkRasterPipeline::xy_to_2pt_conical_well_behaved, ctx);
+    } else if (fFocalData.isSwapped() || 1 - fFocalData.fFocalX < 0) {
+        p->append(SkRasterPipeline::xy_to_2pt_conical_smaller, ctx);
+    } else {
+        p->append(SkRasterPipeline::xy_to_2pt_conical_greater, ctx);
+    }
+
+    if (!fFocalData.isWellBehaved()) {
+        p->append(SkRasterPipeline::mask_2pt_conical_degenerates, ctx);
+    }
+    if (1 - fFocalData.fFocalX < 0) {
+        p->append(SkRasterPipeline::negate_x);
+    }
+    if (!fFocalData.isNativelyFocal()) {
+        p->append(SkRasterPipeline::alter_2pt_conical_compensate_focal, ctx);
+    }
+    if (fFocalData.isSwapped()) {
+        p->append(SkRasterPipeline::alter_2pt_conical_unswap);
+    }
+    if (!fFocalData.isWellBehaved()) {
+        postPipeline->append(SkRasterPipeline::apply_vector_mask, &ctx->fMask);
+    }
+#endif
 }