Correctly distinguish hairline paths in the SW path renderer's cache

We typically don't get hairline paths in the SW path renderer, but an
upcoming change (https://skia-review.googlesource.com/c/skia/+/142272)
makes it possible. Therefore, we need to distinguish between hairline
and filled paths. Additionally, the CPU renderer draws hairline paths
differently with round/square caps (extended by 1/2 pixel) than with
butt caps, so include that in the key, too.

Bug: skia:8188
Change-Id: I223c11f62ef1c13f0488f2ca8c19a19875fc3d9f
Reviewed-on: https://skia-review.googlesource.com/143540
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/src/gpu/GrSoftwarePathRenderer.cpp b/src/gpu/GrSoftwarePathRenderer.cpp
index 213dbdb..8b02273 100644
--- a/src/gpu/GrSoftwarePathRenderer.cpp
+++ b/src/gpu/GrSoftwarePathRenderer.cpp
@@ -291,31 +291,33 @@
         SkScalar kx = args.fViewMatrix->get(SkMatrix::kMSkewX);
         SkScalar ky = args.fViewMatrix->get(SkMatrix::kMSkewY);
         static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
+        GrUniqueKey::Builder builder(&maskKey, kDomain, 5 + args.fShape->unstyledKeySize(),
+                                     "SW Path Mask");
 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
         // Fractional translate does not affect caching on Android. This is done for better cache
         // hit ratio and speed, but it is matching HWUI behavior, which doesn't consider the matrix
         // at all when caching paths.
-        GrUniqueKey::Builder builder(&maskKey, kDomain, 4 + args.fShape->unstyledKeySize(),
-                                     "SW Path Mask");
+        SkFixed fracX = 0;
+        SkFixed fracY = 0;
 #else
         SkScalar tx = args.fViewMatrix->get(SkMatrix::kMTransX);
         SkScalar ty = args.fViewMatrix->get(SkMatrix::kMTransY);
         // Allow 8 bits each in x and y of subpixel positioning.
         SkFixed fracX = SkScalarToFixed(SkScalarFraction(tx)) & 0x0000FF00;
         SkFixed fracY = SkScalarToFixed(SkScalarFraction(ty)) & 0x0000FF00;
-        GrUniqueKey::Builder builder(&maskKey, kDomain, 5 + args.fShape->unstyledKeySize(),
-                                     "SW Path Mask");
 #endif
         builder[0] = SkFloat2Bits(sx);
         builder[1] = SkFloat2Bits(sy);
         builder[2] = SkFloat2Bits(kx);
         builder[3] = SkFloat2Bits(ky);
-#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
-        args.fShape->writeUnstyledKey(&builder[4]);
-#else
-        builder[4] = fracX | (fracY >> 8);
+        // Distinguish between hairline and filled paths. For hairlines, we also need to include
+        // the cap. (SW grows hairlines by 0.5 pixel with round and square caps). Note that
+        // stroke-and-fill of hairlines is turned into pure fill by SkStrokeRec, so this covers
+        // all cases we might see.
+        uint32_t styleBits = args.fShape->style().isSimpleHairline() ?
+                             ((args.fShape->style().strokeRec().getCap() << 1) | 1) : 0;
+        builder[4] = fracX | (fracY >> 8) | (styleBits << 16);
         args.fShape->writeUnstyledKey(&builder[5]);
-#endif
     }
 
     sk_sp<GrTextureProxy> proxy;