Make AALinearizingConvexPathRenderer able to handle stroke and fill

This is intended to catch stroke-and-fill convex paths with potentially small stroke widths (e.g., .1).

It does have the disconcerting side effect of changing bevel-joined stroke-and-filled degenerate-single-line-rects into plain rects (w/o triangular end-caps).

GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2301353004

Committed: https://skia.googlesource.com/skia/+/522bcd99fa65a8abd130880f59b500cf367d0845
Review-Url: https://codereview.chromium.org/2301353004
diff --git a/src/gpu/batches/GrAAConvexTessellator.cpp b/src/gpu/batches/GrAAConvexTessellator.cpp
index af3ce89..2fc33a8 100644
--- a/src/gpu/batches/GrAAConvexTessellator.cpp
+++ b/src/gpu/batches/GrAAConvexTessellator.cpp
@@ -32,11 +32,17 @@
 // dot product above which we consider two adjacent curves to be part of the "same" curve
 static const SkScalar kCurveConnectionThreshold = 0.8f;
 
-static SkScalar intersect(const SkPoint& p0, const SkPoint& n0,
-                          const SkPoint& p1, const SkPoint& n1) {
+static bool intersect(const SkPoint& p0, const SkPoint& n0,
+                      const SkPoint& p1, const SkPoint& n1,
+                      SkScalar* t) {
     const SkPoint v = p1 - p0;
     SkScalar perpDot = n0.fX * n1.fY - n0.fY * n1.fX;
-    return (v.fX * n1.fY - v.fY * n1.fX) / perpDot;
+    if (SkScalarNearlyZero(perpDot)) {
+        return false;
+    }
+    *t = (v.fX * n1.fY - v.fY * n1.fX) / perpDot;
+    SkASSERT(SkScalarIsFinite(*t));
+    return true;
 }
 
 // This is a special case version of intersect where we have the vector
@@ -218,7 +224,45 @@
 
     SkScalar coverage = 1.0f;
     SkScalar scaleFactor = 0.0f;
-    if (fStrokeWidth >= 0.0f) {
+
+    if (SkStrokeRec::kStrokeAndFill_Style == fStyle) {
+        SkASSERT(m.isSimilarity());
+        scaleFactor = m.getMaxScale(); // x and y scale are the same
+        SkScalar effectiveStrokeWidth = scaleFactor * fStrokeWidth;
+        Ring outerStrokeAndAARing;
+        this->createOuterRing(fInitialRing,
+                              effectiveStrokeWidth / 2 + kAntialiasingRadius, 0.0,
+                              &outerStrokeAndAARing);
+
+        // discard all the triangles added between the originating ring and the new outer ring
+        fIndices.rewind();
+
+        outerStrokeAndAARing.init(*this);
+
+        outerStrokeAndAARing.makeOriginalRing();
+
+        // Add the outer stroke ring's normals to the originating ring's normals
+        // so it can also act as an originating ring
+        fNorms.setCount(fNorms.count() + outerStrokeAndAARing.numPts());
+        for (int i = 0; i < outerStrokeAndAARing.numPts(); ++i) {
+            SkASSERT(outerStrokeAndAARing.index(i) < fNorms.count());
+            fNorms[outerStrokeAndAARing.index(i)] = outerStrokeAndAARing.norm(i);
+        }
+
+        // the bisectors are only needed for the computation of the outer ring
+        fBisectors.rewind();
+
+        Ring* insetAARing;
+        this->createInsetRings(outerStrokeAndAARing,
+                               0.0f, 0.0f, 2*kAntialiasingRadius, 1.0f,
+                               &insetAARing);
+
+        SkDEBUGCODE(this->validate();)
+        return true;
+    }
+
+    if (SkStrokeRec::kStroke_Style == fStyle) {
+        SkASSERT(fStrokeWidth >= 0.0f);
         SkASSERT(m.isSimilarity());
         scaleFactor = m.getMaxScale(); // x and y scale are the same
         SkScalar effectiveStrokeWidth = scaleFactor * fStrokeWidth;
@@ -235,15 +279,16 @@
 
     // the bisectors are only needed for the computation of the outer ring
     fBisectors.rewind();
-    if (fStrokeWidth >= 0.0f && fInitialRing.numPts() > 2) {
+    if (SkStrokeRec::kStroke_Style == fStyle && fInitialRing.numPts() > 2) {
+        SkASSERT(fStrokeWidth >= 0.0f);
         SkScalar effectiveStrokeWidth = scaleFactor * fStrokeWidth;
         Ring* insetStrokeRing;
         SkScalar strokeDepth = effectiveStrokeWidth / 2 - kAntialiasingRadius;
         if (this->createInsetRings(fInitialRing, 0.0f, coverage, strokeDepth, coverage,
-                             &insetStrokeRing)) {
+                                   &insetStrokeRing)) {
             Ring* insetAARing;
             this->createInsetRings(*insetStrokeRing, strokeDepth, coverage, strokeDepth +
-                             kAntialiasingRadius * 2, 0.0f, &insetAARing);
+                                   kAntialiasingRadius * 2, 0.0f, &insetAARing);
         }
     } else {
         Ring* insetAARing;
@@ -390,7 +435,7 @@
         this->computeBisectors();
     } else if (this->numPts() == 2) {
         // We've got two points, so we're degenerate.
-        if (fStrokeWidth < 0.0f) {
+        if (fStyle == SkStrokeRec::kFill_Style) {
             // it's a fill, so we don't need to worry about degenerate paths
             return false;
         }
@@ -586,7 +631,7 @@
 // Something went wrong in the creation of the next ring. If we're filling the shape, just go ahead
 // and fan it.
 void GrAAConvexTessellator::terminate(const Ring& ring) {
-    if (fStrokeWidth < 0.0f) {
+    if (fStyle != SkStrokeRec::kStroke_Style) {
         this->fanRing(ring);
     }
 }
@@ -616,8 +661,14 @@
 
     for (int cur = 0; cur < lastRing.numPts(); ++cur) {
         int next = (cur + 1) % lastRing.numPts();
-        SkScalar t = intersect(this->point(lastRing.index(cur)),  lastRing.bisector(cur),
-                               this->point(lastRing.index(next)), lastRing.bisector(next));
+
+        SkScalar t;
+        bool result = intersect(this->point(lastRing.index(cur)),  lastRing.bisector(cur),
+                                this->point(lastRing.index(next)), lastRing.bisector(next),
+                                &t);
+        if (!result) {
+            continue;
+        }
         SkScalar dist = -t * lastRing.norm(cur).dot(lastRing.bisector(cur));
 
         if (minDist > dist) {
@@ -745,8 +796,8 @@
         this->addTri(lastRing.index(i), dst[next], dst[i]);
     }
 
-    if (done && fStrokeWidth < 0.0f) {
-        // fill
+    if (done && fStyle != SkStrokeRec::kStroke_Style) {
+        // fill or stroke-and-fill
         this->fanRing(*nextRing);
     }
 
@@ -860,7 +911,7 @@
             return;
         }
     }
-    SkScalar initialRingCoverage = fStrokeWidth < 0.0f ? 0.5f : 1.0f;
+    SkScalar initialRingCoverage = (SkStrokeRec::kFill_Style == fStyle) ? 0.5f : 1.0f;
     this->addPt(p, 0.0f, initialRingCoverage, false, curve);
     if (this->numPts() > 1) {
         *fNorms.push() = fPts.top() - fPts[fPts.count()-2];
diff --git a/src/gpu/batches/GrAAConvexTessellator.h b/src/gpu/batches/GrAAConvexTessellator.h
index 2683147..4ba3a9c 100644
--- a/src/gpu/batches/GrAAConvexTessellator.h
+++ b/src/gpu/batches/GrAAConvexTessellator.h
@@ -12,6 +12,7 @@
 #include "SkPaint.h"
 #include "SkPoint.h"
 #include "SkScalar.h"
+#include "SkStrokeRec.h"
 #include "SkTDArray.h"
 
 class SkCanvas;
@@ -31,11 +32,13 @@
 // computeDepthFromEdge requests.
 class GrAAConvexTessellator {
 public:
-    GrAAConvexTessellator(SkScalar strokeWidth = -1.0f,
+    GrAAConvexTessellator(SkStrokeRec::Style style = SkStrokeRec::kFill_Style,
+                          SkScalar strokeWidth = -1.0f,
                           SkPaint::Join join = SkPaint::Join::kBevel_Join,
                           SkScalar miterLimit = 0.0f)
         : fSide(SkPoint::kOn_Side)
         , fStrokeWidth(strokeWidth)
+        , fStyle(style)
         , fJoin(join)
         , fMiterLimit(miterLimit) {
     }
@@ -136,6 +139,13 @@
             pt->fOrigEdgeId = origEdgeId;
         }
 
+        // Upgrade this ring so that it can behave like an originating ring
+        void makeOriginalRing() {
+            for (int i = 0; i < fPts.count(); ++i) {
+                fPts[i].fOrigEdgeId = fPts[i].fIndex;
+            }            
+        }
+
         // init should be called after all the indices have been added (via addIdx)
         void init(const GrAAConvexTessellator& tess);
         void init(const SkTDArray<SkVector>& norms, const SkTDArray<SkVector>& bisectors);
@@ -267,8 +277,9 @@
 #endif
     CandidateVerts        fCandidateVerts;
 
-    // < 0 means filling rather than stroking
+    // the stroke width is only used for stroke or stroke-and-fill styles
     SkScalar              fStrokeWidth;
+    SkStrokeRec::Style    fStyle;
 
     SkPaint::Join         fJoin;
 
diff --git a/src/gpu/batches/GrAALinearizingConvexPathRenderer.cpp b/src/gpu/batches/GrAALinearizingConvexPathRenderer.cpp
index 20d93d8..c2873b6 100644
--- a/src/gpu/batches/GrAALinearizingConvexPathRenderer.cpp
+++ b/src/gpu/batches/GrAALinearizingConvexPathRenderer.cpp
@@ -50,12 +50,17 @@
         return false;
     }
     const SkStrokeRec& stroke = args.fShape->style().strokeRec();
-    if (stroke.getStyle() == SkStrokeRec::kStroke_Style) {
+
+    if (stroke.getStyle() == SkStrokeRec::kStroke_Style ||
+        stroke.getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
         if (!args.fViewMatrix->isSimilarity()) {
             return false;
         }
         SkScalar strokeWidth = args.fViewMatrix->getMaxScale() * stroke.getWidth();
-        return strokeWidth >= 1.0f && strokeWidth <= kMaxStrokeWidth &&
+        if (strokeWidth < 1.0f && stroke.getStyle() == SkStrokeRec::kStroke_Style) {
+            return false;
+        }
+        return strokeWidth <= kMaxStrokeWidth &&
                args.fShape->knownToBeClosed() &&
                stroke.getJoin() != SkPaint::Join::kRound_Join;
     }
@@ -126,9 +131,11 @@
                                 const SkMatrix& viewMatrix,
                                 const SkPath& path,
                                 SkScalar strokeWidth,
+                                SkStrokeRec::Style style,
                                 SkPaint::Join join,
                                 SkScalar miterLimit) : INHERITED(ClassID()) {
-        fGeoData.emplace_back(Geometry{color, viewMatrix, path, strokeWidth, join, miterLimit});
+        fGeoData.emplace_back(Geometry{ color, viewMatrix, path,
+                                        strokeWidth, style, join, miterLimit });
 
         // compute bounds
         SkRect bounds = path.getBounds();
@@ -229,7 +236,8 @@
         uint16_t* indices = (uint16_t*) sk_malloc_throw(maxIndices * sizeof(uint16_t));
         for (int i = 0; i < instanceCount; i++) {
             const Geometry& args = fGeoData[i];
-            GrAAConvexTessellator tess(args.fStrokeWidth, args.fJoin, args.fMiterLimit);
+            GrAAConvexTessellator tess(args.fStyle, args.fStrokeWidth,
+                                       args.fJoin, args.fMiterLimit);
 
             if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
                 continue;
@@ -309,6 +317,7 @@
         SkMatrix fViewMatrix;
         SkPath fPath;
         SkScalar fStrokeWidth;
+        SkStrokeRec::Style fStyle;
         SkPaint::Join fJoin;
         SkScalar fMiterLimit;
     };
@@ -324,6 +333,7 @@
                               "GrAALinearizingConvexPathRenderer::onDrawPath");
     SkASSERT(!args.fDrawContext->isUnifiedMultisampled());
     SkASSERT(!args.fShape->isEmpty());
+    SkASSERT(!args.fShape->style().pathEffect());
 
     SkPath path;
     args.fShape->asPath(&path);
@@ -335,8 +345,9 @@
 
     SkAutoTUnref<GrDrawBatch> batch(new AAFlatteningConvexPathBatch(args.fPaint->getColor(),
                                                                     *args.fViewMatrix,
-                                                                    path, strokeWidth, join,
-                                                                    miterLimit));
+                                                                    path, strokeWidth,
+                                                                    stroke.getStyle(),
+                                                                    join, miterLimit));
 
     GrPipelineBuilder pipelineBuilder(*args.fPaint);
     pipelineBuilder.setUserStencil(args.fUserStencilSettings);
@@ -354,10 +365,29 @@
     GrColor color = GrRandomColor(random);
     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
     SkPath path = GrTest::TestPathConvex(random);
-    SkScalar strokeWidth = random->nextBool() ? -1.f : 2.f;
+
+    SkStrokeRec::Style styles[3] = { SkStrokeRec::kFill_Style,
+                                     SkStrokeRec::kStroke_Style, 
+                                     SkStrokeRec::kStrokeAndFill_Style };
+
+    SkStrokeRec::Style style = styles[random->nextU() % 3];
+
+    SkScalar strokeWidth = -1.f;
     SkPaint::Join join = SkPaint::kMiter_Join;
     SkScalar miterLimit = 0.5f;
-    return new AAFlatteningConvexPathBatch(color, viewMatrix, path, strokeWidth, join, miterLimit);
+
+    if (SkStrokeRec::kFill_Style != style) {
+        strokeWidth = random->nextRangeF(1.0f, 10.0f);
+        if (random->nextBool()) {
+            join = SkPaint::kMiter_Join;
+        } else {
+            join = SkPaint::kBevel_Join;
+        }
+        miterLimit = random->nextRangeF(0.5f, 2.0f);
+    }
+
+    return new AAFlatteningConvexPathBatch(color, viewMatrix, path, strokeWidth, 
+                                           style, join, miterLimit);
 }
 
 #endif