ccpr: Skip extremely wide strokes

Instead let them be converted to fill paths and drawn by the CCPR filler.

Bug: skia:8360
Change-Id: Ide3e1cc65e2cf103f2eee9854a1a444fe69ba7fd
Reviewed-on: https://skia-review.googlesource.com/153726
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/ccpr/GrCCDrawPathsOp.cpp b/src/gpu/ccpr/GrCCDrawPathsOp.cpp
index 2ec8379..1ec9493 100644
--- a/src/gpu/ccpr/GrCCDrawPathsOp.cpp
+++ b/src/gpu/ccpr/GrCCDrawPathsOp.cpp
@@ -31,8 +31,6 @@
 std::unique_ptr<GrCCDrawPathsOp> GrCCDrawPathsOp::Make(
         GrContext* context, const SkIRect& clipIBounds, const SkMatrix& m, const GrShape& shape,
         GrPaint&& paint) {
-    static constexpr int kPathCropThreshold = GrCoverageCountingPathRenderer::kPathCropThreshold;
-
     SkRect conservativeDevBounds;
     m.mapRect(&conservativeDevBounds, shape.bounds());
 
@@ -40,23 +38,14 @@
     float strokeDevWidth = 0;
     float conservativeInflationRadius = 0;
     if (!stroke.isFillStyle()) {
-        if (stroke.isHairlineStyle()) {
-            strokeDevWidth = 1;
-        } else {
-            SkASSERT(m.isSimilarity());  // Otherwise matrixScaleFactor = m.getMaxScale().
-            float matrixScaleFactor = SkVector::Length(m.getScaleX(), m.getSkewY());
-            strokeDevWidth = stroke.getWidth() * matrixScaleFactor;
-        }
-        // Inflate for a minimum stroke width of 1. In some cases when the stroke is less than 1px
-        // wide, we may inflate it to 1px and instead reduce the opacity.
-        conservativeInflationRadius = SkStrokeRec::GetInflationRadius(
-                stroke.getJoin(), stroke.getMiter(), stroke.getCap(), SkTMax(strokeDevWidth, 1.f));
+        strokeDevWidth = GrCoverageCountingPathRenderer::GetStrokeDevWidth(
+                m, stroke, &conservativeInflationRadius);
         conservativeDevBounds.outset(conservativeInflationRadius, conservativeInflationRadius);
     }
 
     std::unique_ptr<GrCCDrawPathsOp> op;
     float conservativeSize = SkTMax(conservativeDevBounds.height(), conservativeDevBounds.width());
-    if (conservativeSize > kPathCropThreshold) {
+    if (conservativeSize > GrCoverageCountingPathRenderer::kPathCropThreshold) {
         // The path is too large. Crop it or analytic AA can run out of fp32 precision.
         SkPath croppedDevPath;
         shape.asPath(&croppedDevPath);
@@ -91,6 +80,12 @@
 std::unique_ptr<GrCCDrawPathsOp> GrCCDrawPathsOp::InternalMake(
         GrContext* context, const SkIRect& clipIBounds, const SkMatrix& m, const GrShape& shape,
         float strokeDevWidth, const SkRect& conservativeDevBounds, GrPaint&& paint) {
+    // The path itself should have been cropped if larger than kPathCropThreshold. If it had a
+    // stroke, that would have further inflated its draw bounds.
+    SkASSERT(SkTMax(conservativeDevBounds.height(), conservativeDevBounds.width()) <
+             GrCoverageCountingPathRenderer::kPathCropThreshold +
+             GrCoverageCountingPathRenderer::kMaxBoundsInflationFromStroke*2 + 1);
+
     SkIRect shapeConservativeIBounds;
     conservativeDevBounds.roundOut(&shapeConservativeIBounds);
 
diff --git a/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp b/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
index 0899690..9d30bb4 100644
--- a/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
+++ b/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
@@ -78,7 +78,8 @@
     SkPath path;
     shape.asPath(&path);
 
-    switch (shape.style().strokeRec().getStyle()) {
+    const SkStrokeRec& stroke = shape.style().strokeRec();
+    switch (stroke.getStyle()) {
         case SkStrokeRec::kFill_Style: {
             SkRect devBounds;
             args.fViewMatrix->mapRect(&devBounds, path.getBounds());
@@ -122,9 +123,21 @@
                 return CanDrawPath::kNo;
             }
             // fallthru
-        case SkStrokeRec::kHairline_Style:
-            // The stroker does not support conics yet.
-            return !SkPathPriv::ConicWeightCnt(path) ? CanDrawPath::kYes : CanDrawPath::kNo;
+        case SkStrokeRec::kHairline_Style: {
+            float inflationRadius;
+            GetStrokeDevWidth(*args.fViewMatrix, stroke, &inflationRadius);
+            if (!(inflationRadius <= kMaxBoundsInflationFromStroke)) {
+                // Let extremely wide strokes be converted to fill paths and drawn by the CCPR
+                // filler instead. (Cast the logic negatively in order to also catch r=NaN.)
+                return CanDrawPath::kNo;
+            }
+            SkASSERT(!SkScalarIsNaN(inflationRadius));
+            if (SkPathPriv::ConicWeightCnt(path)) {
+                // The stroker does not support conics yet.
+                return CanDrawPath::kNo;
+            }
+            return CanDrawPath::kYes;
+        }
 
         case SkStrokeRec::kStrokeAndFill_Style:
             return CanDrawPath::kNo;
@@ -323,3 +336,24 @@
     }
     out->setIsVolatile(true);
 }
+
+float GrCoverageCountingPathRenderer::GetStrokeDevWidth(const SkMatrix& m,
+                                                        const SkStrokeRec& stroke,
+                                                        float* inflationRadius) {
+    float strokeDevWidth;
+    if (stroke.isHairlineStyle()) {
+        strokeDevWidth = 1;
+    } else {
+        SkASSERT(SkStrokeRec::kStroke_Style == stroke.getStyle());
+        SkASSERT(m.isSimilarity());  // Otherwise matrixScaleFactor = m.getMaxScale().
+        float matrixScaleFactor = SkVector::Length(m.getScaleX(), m.getSkewY());
+        strokeDevWidth = stroke.getWidth() * matrixScaleFactor;
+    }
+    if (inflationRadius) {
+        // Inflate for a minimum stroke width of 1. In some cases when the stroke is less than 1px
+        // wide, we may inflate it to 1px and instead reduce the opacity.
+        *inflationRadius = SkStrokeRec::GetInflationRadius(
+                stroke.getJoin(), stroke.getMiter(), stroke.getCap(), SkTMax(strokeDevWidth, 1.f));
+    }
+    return strokeDevWidth;
+}
diff --git a/src/gpu/ccpr/GrCoverageCountingPathRenderer.h b/src/gpu/ccpr/GrCoverageCountingPathRenderer.h
index bc336a5..1d3bfd8 100644
--- a/src/gpu/ccpr/GrCoverageCountingPathRenderer.h
+++ b/src/gpu/ccpr/GrCoverageCountingPathRenderer.h
@@ -76,6 +76,13 @@
 
     static void CropPath(const SkPath&, const SkIRect& cropbox, SkPath* out);
 
+    // Maximum inflation of path bounds due to stroking (from width, miter, caps). Strokes wider
+    // than this will be converted to fill paths and drawn by the CCPR filler instead.
+    static constexpr float kMaxBoundsInflationFromStroke = 4096;
+
+    static float GetStrokeDevWidth(const SkMatrix&, const SkStrokeRec&,
+                                   float* inflationRadius = nullptr);
+
 private:
     GrCoverageCountingPathRenderer(AllowCaching);