Use DAA for small cubics and non-convex paths that fit into a mask

I forgot to benchmark svgs and it turns out that DAA is specifically
good for the small cubics and small non-convex paths in svgs. This
should make our svg performance fast again:

    2.84% faster in svgparse_Florida-StateSeal.svg_1
    2.90% faster in svgparse_NewYork-StateSeal.svg_1
    2.95% faster in svgparse_Seal_of_Texas.svg_1
    3.05% faster in car.svg_1
    3.53% faster in svgparse_Vermont_state_seal.svg_1
    3.68% faster in svgparse_Wyoming-StateSeal.svg_1
    4.88% faster in svgparse_Minnesota-StateSeal.svg_1
    5.22% faster in svgparse_NewMexico-StateSeal.svg_1
    6.49% faster in svgparse_fsm.svg_1


Bug: skia:
Change-Id: Ia149944443d72c12c3dda178cb5ebc89d6d0bf18
Reviewed-on: https://skia-review.googlesource.com/116185
Reviewed-by: Cary Clark <caryclark@google.com>
Commit-Queue: Yuqian Li <liyuqian@google.com>
diff --git a/src/core/SkCoverageDelta.cpp b/src/core/SkCoverageDelta.cpp
index a6bb1dd..defdcf2 100644
--- a/src/core/SkCoverageDelta.cpp
+++ b/src/core/SkCoverageDelta.cpp
@@ -46,6 +46,10 @@
 }
 
 bool SkCoverageDeltaMask::CanHandle(const SkIRect& bounds) {
+    // Return early if either width or height is very large because width * height might overflow.
+    if (bounds.width() >= MAX_MASK_SIZE || bounds.height() >= MAX_MASK_SIZE) {
+        return false;
+    }
     // Expand width so we don't have to worry about the boundary
     return ExpandWidth(bounds.width()) * bounds.height() + PADDING * 2 < MAX_MASK_SIZE;
 }
diff --git a/src/core/SkScan_AntiPath.cpp b/src/core/SkScan_AntiPath.cpp
index ad181a7..2eaa89e 100644
--- a/src/core/SkScan_AntiPath.cpp
+++ b/src/core/SkScan_AntiPath.cpp
@@ -12,6 +12,7 @@
 #include "SkBlitter.h"
 #include "SkRegion.h"
 #include "SkAntiRun.h"
+#include "SkCoverageDelta.h"
 
 #define SHIFT   SK_SUPERSAMPLE_SHIFT
 #define SCALE   (1 << SHIFT)
@@ -583,6 +584,19 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+static SkIRect safeRoundOut(const SkRect& src) {
+    // roundOut will pin huge floats to max/min int
+    SkIRect dst = src.roundOut();
+
+    // intersect with a smaller huge rect, so the rect will not be considered empty for being
+    // too large. e.g. { -SK_MaxS32 ... SK_MaxS32 } is considered empty because its width
+    // exceeds signed 32bit.
+    const int32_t limit = SK_MaxS32 >> SK_SUPERSAMPLE_SHIFT;
+    (void)dst.intersect({ -limit, -limit, limit, limit});
+
+    return dst;
+}
+
 static bool ShouldUseDAA(const SkPath& path) {
     if (gSkForceDeltaAA) {
         return true;
@@ -597,12 +611,18 @@
 #else
     constexpr int kSampleSize = 8;
     constexpr SkScalar kComplexityThreshold = 0.25;
+    constexpr SkScalar kSmallCubicThreshold = 16;
 
     int n = path.countPoints();
     if (path.isConvex() || n < kSampleSize) {
         return false;
     }
 
+    // DAA is fast with mask
+    if (SkCoverageDeltaMask::CanHandle(safeRoundOut(path.getBounds()))) {
+        return true;
+    }
+
     SkScalar sumLength = 0;
     SkPoint lastPoint = path.getPoint(0);
     for(int i = 1; i < kSampleSize; ++i) {
@@ -610,11 +630,27 @@
         sumLength += SkPoint::Distance(lastPoint, point);
         lastPoint = point;
     }
+    SkScalar avgLength = sumLength / (kSampleSize - 1);
+
+    // DAA is much faster in small cubics (since we don't have to chop them).
+    // If there are many cubics, and the average length if small, use DAA.
+    if (avgLength < kSmallCubicThreshold) {
+        uint8_t sampleVerbs[kSampleSize];
+        int verbCount = SkTMin(kSampleSize, path.getVerbs(sampleVerbs, kSampleSize));
+        int cubicCount = 0;
+        for(int i = 0; i < verbCount; ++i) {
+            cubicCount += (sampleVerbs[i] == SkPath::kCubic_Verb);
+        }
+        if (cubicCount * 2 >= verbCount) {
+            return true;
+        }
+    }
+
     SkScalar diagonal = SkPoint::Length(path.getBounds().width(), path.getBounds().height());
 
     // On average, what's the distance between two consecutive points; the number is normalized
     // to a range of [0, 1] where 1 corresponds to the maximum length of the diagonal.
-    SkScalar sampleSpan = sumLength / (kSampleSize - 1) / diagonal;
+    SkScalar sampleSpan = avgLength / diagonal;
 
 
     // If the path is consist of random line segments, the number of intersections should be
@@ -687,19 +723,6 @@
            overflows_short_shift(rect.fBottom, shift);
 }
 
-static SkIRect safeRoundOut(const SkRect& src) {
-    // roundOut will pin huge floats to max/min int
-    SkIRect dst = src.roundOut();
-
-    // intersect with a smaller huge rect, so the rect will not be considered empty for being
-    // too large. e.g. { -SK_MaxS32 ... SK_MaxS32 } is considered empty because its width
-    // exceeds signed 32bit.
-    const int32_t limit = SK_MaxS32 >> SK_SUPERSAMPLE_SHIFT;
-    (void)dst.intersect({ -limit, -limit, limit, limit});
-
-    return dst;
-}
-
 void SkScan::AntiFillPath(const SkPath& path, const SkRegion& origClip,
                           SkBlitter* blitter, bool forceRLE, SkDAARecord* daaRecord) {
     if (origClip.isEmpty()) {