Add stroking support to distance field path renderer

Also slightly increases sizes of paths accepted for distance field
caching.

Committed: https://skia.googlesource.com/skia/+/5ce76efd1c847308c7bcac17bd87d567c42cd786

Committed: https://skia.googlesource.com/skia/+/73ee6770260dbeeabc4a78eee4f13533f0031211

Review URL: https://codereview.chromium.org/1460873002
diff --git a/src/gpu/GrPathRenderer.h b/src/gpu/GrPathRenderer.h
index 25a2a68..7ada6d1 100644
--- a/src/gpu/GrPathRenderer.h
+++ b/src/gpu/GrPathRenderer.h
@@ -115,6 +115,7 @@
      * fTarget                The target that the path will be rendered to
      * fResourceProvider      The resource provider for creating gpu resources to render the path
      * fPipelineBuilder       The pipelineBuilder
+     * fColor                 Color to render with
      * fViewMatrix            The viewMatrix
      * fPath                  the path to draw.
      * fStroke                the stroke information (width, join, cap)
diff --git a/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp b/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp
index ee5fdb8..bb388d7 100644
--- a/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp
+++ b/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp
@@ -38,7 +38,7 @@
 
 // mip levels
 static const int kSmallMIP = 32;
-static const int kMediumMIP = 72;
+static const int kMediumMIP = 73;
 static const int kLargeMIP = 162;
 
 // Callback to clear out internal path cache when eviction occurs
@@ -84,10 +84,9 @@
 bool GrAADistanceFieldPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
 
     // TODO: Support inverse fill
-    // TODO: Support strokes
     if (!args.fShaderCaps->shaderDerivativeSupport() || !args.fAntiAlias ||
-        args.fPath->isInverseFillType() || args.fPath->isVolatile() ||
-        !args.fStroke->isFillStyle()) {
+        SkStrokeRec::kHairline_Style == args.fStroke->getStyle() ||
+        args.fPath->isInverseFillType() || args.fPath->isVolatile()) {
         return false;
     }
 
@@ -96,12 +95,22 @@
         return false;
     }
     
-    // only support paths smaller than 64x64, scaled to less than 256x256
+    // only support paths with bounds within kMediumMIP by kMediumMIP,
+    // scaled to have bounds within 2.0f*kLargeMIP by 2.0f*kLargeMIP
     // the goal is to accelerate rendering of lots of small paths that may be scaling
     SkScalar maxScale = args.fViewMatrix->getMaxScale();
     const SkRect& bounds = args.fPath->getBounds();
     SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
-    return maxDim < 64.f && maxDim * maxScale < 256.f;
+    // Approximate stroked size by adding the maximum of the stroke width or 2x the miter limit
+    if (!args.fStroke->isFillStyle()) {
+        SkScalar extraWidth = args.fStroke->getWidth();
+        if (SkPaint::kMiter_Join == args.fStroke->getJoin()) {
+            extraWidth = SkTMax(extraWidth, 2.0f*args.fStroke->getMiter());
+        }
+        maxDim += extraWidth;
+    }
+    
+    return maxDim <= kMediumMIP && maxDim * maxScale <= 2.0f*kLargeMIP;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -118,8 +127,20 @@
     typedef GrAADistanceFieldPathRenderer::PathDataList PathDataList;
 
     struct Geometry {
-        Geometry(const SkStrokeRec& stroke) : fStroke(stroke) {}
+        Geometry(const SkStrokeRec& stroke) : fStroke(stroke) {
+            if (!stroke.needToApply()) {
+                // purify unused values to ensure binary equality
+                fStroke.setStrokeParams(SkPaint::kDefault_Cap, SkPaint::kDefault_Join,
+                                        SkIntToScalar(4));
+                if (fStroke.getWidth() < 0) {
+                    fStroke.setStrokeStyle(-1.0f);
+                }
+            }
+        }
         SkPath fPath;
+        // The unique ID of the path involved in this draw. This may be different than the ID
+        // in fPath since that path may have resulted from a SkStrokeRec::applyToPath call.
+        uint32_t fGenID;
         SkStrokeRec fStroke;
         bool fAntiAlias;
         PathData* fPathData;
@@ -225,8 +246,7 @@
             }
 
             // check to see if path is cached
-            // TODO: handle stroked vs. filled version of same path
-            PathData::Key key = { args.fPath.getGenerationID(), desiredDimension };
+            PathData::Key key(args.fGenID, desiredDimension, args.fStroke);
             args.fPathData = fPathCache->find(key);
             if (nullptr == args.fPathData || !atlas->hasID(args.fPathData->fID)) {
                 // Remove the stale cache entry
@@ -244,6 +264,7 @@
                                           atlas,
                                           args.fPathData,
                                           args.fPath,
+                                          args.fGenID,
                                           args.fStroke,
                                           args.fAntiAlias,
                                           desiredDimension,
@@ -301,8 +322,9 @@
                         GrBatchAtlas* atlas,
                         PathData* pathData,
                         const SkPath& path,
-                        const SkStrokeRec&
-                        stroke, bool antiAlias,
+                        uint32_t genID,
+                        const SkStrokeRec& stroke,
+                        bool antiAlias,
                         uint32_t dimension,
                         SkScalar scale) {
         const SkRect& bounds = path.getBounds();
@@ -333,41 +355,25 @@
         drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad);
 
         // setup bitmap backing
-        // Now translate so the bound's UL corner is at the origin
-        drawMatrix.postTranslate(-devPathBounds.fLeft * SK_Scalar1,
-                                 -devPathBounds.fTop * SK_Scalar1);
-        SkIRect pathBounds = SkIRect::MakeWH(devPathBounds.width(),
-                                             devPathBounds.height());
-
+        SkASSERT(devPathBounds.fLeft == 0);
+        SkASSERT(devPathBounds.fTop == 0);
         SkAutoPixmapStorage dst;
-        if (!dst.tryAlloc(SkImageInfo::MakeA8(pathBounds.width(),
-                                              pathBounds.height()))) {
+        if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(),
+                                              devPathBounds.height()))) {
             return false;
         }
         sk_bzero(dst.writable_addr(), dst.getSafeSize());
 
         // rasterize path
         SkPaint paint;
-        if (stroke.isHairlineStyle()) {
-            paint.setStyle(SkPaint::kStroke_Style);
-            paint.setStrokeWidth(SK_Scalar1);
-        } else {
-            if (stroke.isFillStyle()) {
-                paint.setStyle(SkPaint::kFill_Style);
-            } else {
-                paint.setStyle(SkPaint::kStroke_Style);
-                paint.setStrokeJoin(stroke.getJoin());
-                paint.setStrokeCap(stroke.getCap());
-                paint.setStrokeWidth(stroke.getWidth());
-            }
-        }
+        paint.setStyle(SkPaint::kFill_Style);
         paint.setAntiAlias(antiAlias);
 
         SkDraw draw;
         sk_bzero(&draw, sizeof(draw));
 
         SkRasterClip rasterClip;
-        rasterClip.setRect(pathBounds);
+        rasterClip.setRect(devPathBounds);
         draw.fRC = &rasterClip;
         draw.fClip = &rasterClip.bwRgn();
         draw.fMatrix = &drawMatrix;
@@ -403,8 +409,7 @@
         }
 
         // add to cache
-        pathData->fKey.fGenID = path.getGenerationID();
-        pathData->fKey.fDimension = dimension;
+        pathData->fKey = PathData::Key(genID, dimension, stroke);
         pathData->fScale = scale;
         pathData->fID = id;
         // change the scaled rect to match the size of the inset distance field
@@ -543,9 +548,17 @@
     }
 
     AADistanceFieldPathBatch::Geometry geometry(*args.fStroke);
-    geometry.fPath = *args.fPath;
+    if (SkStrokeRec::kFill_Style == args.fStroke->getStyle()) {
+        geometry.fPath = *args.fPath;
+    } else {
+        args.fStroke->applyToPath(&geometry.fPath, *args.fPath);
+    }
     geometry.fAntiAlias = args.fAntiAlias;
-
+    // Note: this is the generation ID of the _original_ path. When a new path is
+    // generated due to stroking it is important that the original path's id is used
+    // for caching.
+    geometry.fGenID = args.fPath->getGenerationID();
+ 
     SkAutoTUnref<GrDrawBatch> batch(AADistanceFieldPathBatch::Create(geometry, args.fColor,
                                                                      *args.fViewMatrix, fAtlas,
                                                                      &fPathCache, &fPathList));
@@ -620,6 +633,7 @@
     AADistanceFieldPathBatch::Geometry geometry(GrTest::TestStrokeRec(random));
     geometry.fPath = GrTest::TestPath(random);
     geometry.fAntiAlias = random->nextBool();
+    geometry.fGenID = random->nextU();
 
     return AADistanceFieldPathBatch::Create(geometry, color, viewMatrix,
                                             gTestStruct.fAtlas,
diff --git a/src/gpu/batches/GrAADistanceFieldPathRenderer.h b/src/gpu/batches/GrAADistanceFieldPathRenderer.h
index 469aeeb..c0e6826 100755
--- a/src/gpu/batches/GrAADistanceFieldPathRenderer.h
+++ b/src/gpu/batches/GrAADistanceFieldPathRenderer.h
@@ -33,13 +33,30 @@
     bool onDrawPath(const DrawPathArgs&) override;
 
     struct PathData {
-        struct Key {
+        class Key {
+        public:
+            // default ctor needed for new of uninitialized PathData
+            // since fStroke has no default ctor
+            Key() 
+                : fGenID(0)
+                , fDimension(0)
+                , fStroke(SkStrokeRec::kFill_InitStyle) {}
+            Key(uint32_t genID, uint32_t dim, const SkStrokeRec& stroke)
+                : fGenID(genID)
+                , fDimension(dim)
+                , fStroke(stroke) {}
+           
+            bool operator==(const Key& other) const {
+                return other.fGenID == fGenID && other.fDimension == fDimension &&
+                       fStroke.hasEqualEffect(other.fStroke);
+            }
+           
+        private:
             uint32_t   fGenID;
             // rendered size for stored path (32x32 max, 64x64 max, 128x128 max)
             uint32_t   fDimension;
-            bool operator==(const Key& other) const {
-                return other.fGenID == fGenID && other.fDimension == fDimension;
-            }
+            // stroking information
+            SkStrokeRec fStroke;
         };
         Key                   fKey;
         SkScalar              fScale;