Improve coverage AA for thin quads

This brings back the 2px wide picture frame approach described in these
slides: go/thin-line-aa. This has been in place when the per-edge-aa
only needed to support rectangles but was scrapped when the code path
was updated to support arbitrary quadrilaterals.

I opted to have the GrQuadPerEdgeAA logic check for degeneracy and
update what it requests for the outset. This scale factor out to a 2px
wide shape makes sense in the context of anti-aliasing, but not so for
the generalized inset/outset logic defined in GrQuadUtils. It would have
been more efficient to implement it there, but would have locked it in
to being just AA inset/outset.

I also updated SkGpuDevice's drawStrokedLine to construct the quad of
the line directly, and to always turn a line path into a rect, instead
of restricting it based on matrix or stroked width. With this new change
the quality of the fill rect is much higher under rotations and
perspective compared to the hairline.

See rect case: https://drive.google.com/file/d/1xwgG5heADcdXYShsDodgbuv2tbHfuBNt/view?usp=sharing
Hairline case: https://drive.google.com/file/d/1duNLxiYLLJhsJ94Uc01rSxjB4ar6_Ud9/view?usp=sharing

Bug: chromium:820987
Change-Id: Ibd58b89a467ad5a61c5479d11259024259f1bb47
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/329418
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
diff --git a/src/gpu/ops/GrQuadPerEdgeAA.cpp b/src/gpu/ops/GrQuadPerEdgeAA.cpp
index 01bc935..6833342 100644
--- a/src/gpu/ops/GrQuadPerEdgeAA.cpp
+++ b/src/gpu/ops/GrQuadPerEdgeAA.cpp
@@ -362,7 +362,20 @@
             fWriteProc(&fVertexWriter, fVertexSpec, deviceQuad, localQuad, coverage, color,
                        geomSubset, uvSubset);
 
-            // Then outer vertices, which use 0.f for their coverage
+            // Then outer vertices, which use 0.f for their coverage. If the inset was degenerate
+            // to a line (had all coverages < 1), tweak the outset distance so the outer frame's
+            // narrow axis reaches out to 2px, which gives better animation under translation.
+            if (coverage[0] < 1.f && coverage[1] < 1.f && coverage[2] < 1.f && coverage[3] < 1.f) {
+                skvx::Vec<4, float> len = fAAHelper.getEdgeLengths();
+                // Using max guards us against trying to scale a degenerate triangle edge of 0 len
+                // up to 2px. The shuffles are so that edge 0's adjustment is based on the lengths
+                // of its connecting edges (1 and 2), and so forth.
+                skvx::Vec<4, float> maxWH = max(skvx::shuffle<1, 0, 3, 2>(len),
+                                                skvx::shuffle<2, 3, 0, 1>(len));
+                // wh + 2e' = 2, so e' = (2 - wh) / 2 => e' = e * (2 - wh). But if w or h > 1, then
+                // 2 - wh < 1 and represents the non-narrow axis so clamp to 1.
+                edgeDistances *= max(1.f, 2.f - maxWH);
+            }
             fAAHelper.outset(edgeDistances, deviceQuad, localQuad);
             fWriteProc(&fVertexWriter, fVertexSpec, deviceQuad, localQuad, kZeroCoverage, color,
                        geomSubset, uvSubset);