Add first class hairline support to tessellated stroking

Bug: skia:10419
Change-Id: I63f000e7b3c5623c1e40c3ce6950c8f5565bc11c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/343477
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/tessellate/GrStrokeOp.h b/src/gpu/tessellate/GrStrokeOp.h
index 84e625f..7cfa216 100644
--- a/src/gpu/tessellate/GrStrokeOp.h
+++ b/src/gpu/tessellate/GrStrokeOp.h
@@ -12,6 +12,8 @@
 #include "include/gpu/GrRecordingContext.h"
 #include "src/gpu/GrSTArenaList.h"
 #include "src/gpu/ops/GrDrawOp.h"
+#include "src/gpu/tessellate/GrStrokeTessellateShader.h"
+#include <array>
 
 class GrStrokeTessellateShader;
 
@@ -34,7 +36,7 @@
                                       bool hasMixedSampledCoverage, GrClampType) override;
     CombineResult onCombineIfPossible(GrOp*, SkArenaAlloc*, const GrCaps&) override;
 
-    void prePreparePrograms(SkArenaAlloc* arena, GrStrokeTessellateShader*,
+    void prePreparePrograms(GrStrokeTessellateShader::Mode, SkArenaAlloc*,
                             const GrSurfaceProxyView&, GrAppliedClip&&,
                             const GrXferProcessor::DstProxyView&, GrXferBarrierFlags,
                             GrLoadOp colorLoadOp, const GrCaps&);
@@ -64,6 +66,29 @@
         return std::max(numCombinedSegments + 1 - numRadialSegments, 0.f);
     }
 
+    // Returns the equivalent tolerances in (pre-viewMatrix) local path space that the tessellator
+    // will use when rendering this stroke.
+    GrStrokeTessellateShader::Tolerances preTransformTolerances() const {
+        std::array<float,2> matrixScales;
+        if (!fViewMatrix.getMinMaxScales(matrixScales.data())) {
+            matrixScales.fill(1);
+        }
+        auto [matrixMinScale, matrixMaxScale] = matrixScales;
+        float localStrokeWidth = fStroke.getWidth();
+        if (fStroke.isHairlineStyle()) {
+            // If the stroke is hairline then the tessellator will operate in post-transform space
+            // instead. But for the sake of CPU methods that need to conservatively approximate the
+            // number of segments to emit, we use localStrokeWidth ~= 1/matrixMinScale.
+            float approxScale = matrixMinScale;
+            // If the matrix has strong skew, don't let the scale shoot off to infinity. (This does
+            // not affect the tessellator; only the CPU methods that approximate the number of
+            // segments to emit.)
+            approxScale = std::max(matrixMinScale, matrixMaxScale * .25f);
+            localStrokeWidth = 1/approxScale;
+        }
+        return GrStrokeTessellateShader::Tolerances(matrixMaxScale, localStrokeWidth);
+    }
+
     const GrAAType fAAType;
     const SkMatrix fViewMatrix;
     const SkStrokeRec fStroke;