Apply hairline optimization only if the path renderer wants it

Make the decision to convert thin, non-hairline paths to hairline
paths at the renderer level.

The current nv_path_rendering implementation does not render
hairlines. Rendering the hairlines with normal renderers cause
unneccessary gl program changes, which is quite slow.

Changes the behavior of non-nv_path_rendering paths to always perform
the optimization if the shape ends up being painted by a renderer that
wants the optimization. Previously the optimization was applied only
when callgraph started with SkCanvas::drawPath.

Applies the optimization for GrAAHairLineRenderer and
GrDefaultPathRenderer.

This changes gm results for dashing3_{msaa4,gpu} and drawlooper_msaa4.

R=bsalomon@google.com, jvanverth@google.com, rmistry@google.com

Author: kkinnunen@nvidia.com

Review URL: https://codereview.chromium.org/38573007

git-svn-id: http://skia.googlecode.com/svn/trunk@12357 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index 0288dee..52891ea 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.cpp
@@ -988,24 +988,11 @@
     }
 }
 
-bool SkDrawTreatAsHairline(const SkPaint& paint, const SkMatrix& matrix,
-                           SkScalar* coverage) {
-    SkASSERT(coverage);
-    if (SkPaint::kStroke_Style != paint.getStyle()) {
-        return false;
-    }
-    SkScalar strokeWidth = paint.getStrokeWidth();
-    if (0 == strokeWidth) {
-        *coverage = SK_Scalar1;
-        return true;
-    }
+bool SkDrawTreatAAStrokeAsHairline(SkScalar strokeWidth, const SkMatrix& matrix,
+                                   SkScalar* coverage) {
+    SkASSERT(strokeWidth > 0);
+    // We need to try to fake a thick-stroke with a modulated hairline.
 
-    // if we get here, we need to try to fake a thick-stroke with a modulated
-    // hairline
-
-    if (!paint.isAntiAlias()) {
-        return false;
-    }
     if (matrix.hasPerspective()) {
         return false;
     }
@@ -1017,7 +1004,9 @@
     SkScalar len0 = fast_len(dst[0]);
     SkScalar len1 = fast_len(dst[1]);
     if (len0 <= SK_Scalar1 && len1 <= SK_Scalar1) {
-        *coverage = SkScalarAve(len0, len1);
+        if (NULL != coverage) {
+            *coverage = SkScalarAve(len0, len1);
+        }
         return true;
     }
     return false;
diff --git a/src/core/SkDrawProcs.h b/src/core/SkDrawProcs.h
index cc2f3ed..6911e5b 100644
--- a/src/core/SkDrawProcs.h
+++ b/src/core/SkDrawProcs.h
@@ -75,12 +75,32 @@
 #endif
 };
 
+bool SkDrawTreatAAStrokeAsHairline(SkScalar strokeWidth, const SkMatrix&,
+                                   SkScalar* coverage);
+
 /**
  *  If the current paint is set to stroke and the stroke-width when applied to
  *  the matrix is <= 1.0, then this returns true, and sets coverage (simulating
  *  a stroke by drawing a hairline with partial coverage). If any of these
  *  conditions are false, then this returns false and coverage is ignored.
  */
-bool SkDrawTreatAsHairline(const SkPaint&, const SkMatrix&, SkScalar* coverage);
+inline bool SkDrawTreatAsHairline(const SkPaint& paint, const SkMatrix& matrix,
+                                  SkScalar* coverage) {
+    if (SkPaint::kStroke_Style != paint.getStyle()) {
+        return false;
+    }
+
+    SkScalar strokeWidth = paint.getStrokeWidth();
+    if (0 == strokeWidth) {
+        *coverage = SK_Scalar1;
+        return true;
+    }
+
+    if (!paint.isAntiAlias()) {
+        return false;
+    }
+
+    return SkDrawTreatAAStrokeAsHairline(strokeWidth, matrix, coverage);
+}
 
 #endif
diff --git a/src/gpu/GrAAHairLinePathRenderer.cpp b/src/gpu/GrAAHairLinePathRenderer.cpp
index 02c4419..3956310 100644
--- a/src/gpu/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/GrAAHairLinePathRenderer.cpp
@@ -759,7 +759,7 @@
     }
     devBounds->set(lines.begin(), lines.count());
     for (int i = 0; i < lineCnt; ++i) {
-        add_line(&lines[2*i], toSrc, drawState->getCoverage(), &verts);
+        add_line(&lines[2*i], toSrc, drawState->getCoverageColor(), &verts);
     }
     // All the verts computed by add_line are within sqrt(1^2 + 0.5^2) of the end points.
     static const SkScalar kSqrtOfOneAndAQuarter = SkFloatToScalar(1.118f);
@@ -839,7 +839,13 @@
                                            const SkStrokeRec& stroke,
                                            const GrDrawTarget* target,
                                            bool antiAlias) const {
-    if (!stroke.isHairlineStyle() || !antiAlias) {
+    if (!antiAlias) {
+        return false;
+    }
+
+    if (!IsStrokeHairlineOrEquivalent(stroke,
+                                      target->getDrawState().getViewMatrix(),
+                                      NULL)) {
         return false;
     }
 
@@ -888,12 +894,20 @@
 }
 
 bool GrAAHairLinePathRenderer::onDrawPath(const SkPath& path,
-                                          const SkStrokeRec&,
+                                          const SkStrokeRec& stroke,
                                           GrDrawTarget* target,
                                           bool antiAlias) {
-
     GrDrawState* drawState = target->drawState();
 
+    SkScalar hairlineCoverage;
+    if (IsStrokeHairlineOrEquivalent(stroke,
+                                     target->getDrawState().getViewMatrix(),
+                                     &hairlineCoverage)) {
+        uint8_t newCoverage = SkScalarRoundToInt(hairlineCoverage *
+                                                 target->getDrawState().getCoverage());
+        target->drawState()->setCoverage(newCoverage);
+    }
+
     SkIRect devClipBounds;
     target->getClip()->getConservativeBounds(drawState->getRenderTarget(), &devClipBounds);
 
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index b0d34fd..cab4414 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -43,10 +43,6 @@
 
 #define BUFFERED_DRAW (c_Defer ? kYes_BufferedDraw : kNo_BufferedDraw)
 
-// When we're using coverage AA but the blend is incompatible (given gpu
-// limitations) should we disable AA or draw wrong?
-#define DISABLE_COVERAGE_AA_FOR_BLEND 1
-
 #ifdef SK_DEBUG
     // change this to a 1 to see notifications when partial coverage fails
     #define GR_DEBUG_PARTIAL_COVERAGE_CHECK 0
@@ -695,14 +691,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-namespace {
-inline bool disable_coverage_aa_for_blend(GrDrawTarget* target) {
-    return DISABLE_COVERAGE_AA_FOR_BLEND && !target->canApplyCoverage();
-}
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
 /*  create a triangle strip that strokes the specified triangle. There are 8
  unique vertices, but we repreat the last 2 to close up. Alternatively we
  could use an indices array, and then only send 8 verts, but not sure that
@@ -746,7 +734,7 @@
     // TODO: remove this ugliness when we drop the fixed-pipe impl
     *useVertexCoverage = false;
     if (!target->getDrawState().canTweakAlphaForCoverage()) {
-        if (disable_coverage_aa_for_blend(target)) {
+        if (target->shouldDisableCoverageAAForBlend()) {
 #ifdef SK_DEBUG
             //GrPrintf("Turning off AA to correctly apply blend.\n");
 #endif
@@ -1034,14 +1022,10 @@
     AutoCheckFlush acf(this);
     GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are, &acf);
 
-    bool useAA = paint.isAntiAlias() &&
-                 !target->getDrawState().getRenderTarget()->isMultisampled() &&
-                 !disable_coverage_aa_for_blend(target);
-
-    if (!fOvalRenderer->drawSimpleRRect(target, this, useAA, rect, stroke)) {
+    if (!fOvalRenderer->drawSimpleRRect(target, this, paint.isAntiAlias(), rect, stroke)) {
         SkPath path;
         path.addRRect(rect);
-        this->internalDrawPath(target, useAA, path, stroke);
+        this->internalDrawPath(target, paint.isAntiAlias(), path, stroke);
     }
 }
 
@@ -1058,14 +1042,10 @@
     AutoCheckFlush acf(this);
     GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are, &acf);
 
-    bool useAA = paint.isAntiAlias() &&
-                 !target->getDrawState().getRenderTarget()->isMultisampled() &&
-                 !disable_coverage_aa_for_blend(target);
-
-    if (!fOvalRenderer->drawOval(target, this, useAA, oval, stroke)) {
+    if (!fOvalRenderer->drawOval(target, this, paint.isAntiAlias(), oval, stroke)) {
         SkPath path;
         path.addOval(oval);
-        this->internalDrawPath(target, useAA, path, stroke);
+        this->internalDrawPath(target, paint.isAntiAlias(), path, stroke);
     }
 }
 
@@ -1091,7 +1071,7 @@
 
     *useVertexCoverage = false;
     if (!target->getDrawState().canTweakAlphaForCoverage()) {
-        if (disable_coverage_aa_for_blend(target)) {
+        if (target->shouldDisableCoverageAAForBlend()) {
             return false;
         } else {
             *useVertexCoverage = true;
@@ -1141,15 +1121,17 @@
     AutoRestoreEffects are;
     AutoCheckFlush acf(this);
     GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are, &acf);
+    GrDrawState* drawState = target->drawState();
 
-    bool useAA = paint.isAntiAlias() && !target->getDrawState().getRenderTarget()->isMultisampled();
-    if (useAA && stroke.getWidth() < 0 && !path.isConvex()) {
+    bool useCoverageAA = paint.isAntiAlias() && !drawState->getRenderTarget()->isMultisampled();
+
+    if (useCoverageAA && stroke.getWidth() < 0 && !path.isConvex()) {
         // Concave AA paths are expensive - try to avoid them for special cases
         bool useVertexCoverage;
         SkRect rects[2];
 
         if (is_nested_rects(target, path, stroke, rects, &useVertexCoverage)) {
-            SkMatrix origViewMatrix = target->getDrawState().getViewMatrix();
+            SkMatrix origViewMatrix = drawState->getViewMatrix();
             GrDrawState::AutoViewMatrixRestore avmr;
             if (!avmr.setIdentity(target->drawState())) {
                 return;
@@ -1167,8 +1149,8 @@
     bool isOval = path.isOval(&ovalRect);
 
     if (!isOval || path.isInverseFillType()
-        || !fOvalRenderer->drawOval(target, this, useAA, ovalRect, stroke)) {
-        this->internalDrawPath(target, useAA, path, stroke);
+        || !fOvalRenderer->drawOval(target, this, paint.isAntiAlias(), ovalRect, stroke)) {
+        this->internalDrawPath(target, paint.isAntiAlias(), path, stroke);
     }
 }
 
@@ -1180,15 +1162,14 @@
     // the src color (either the input alpha or in the frag shader) to implement
     // aa. If we have some future driver-mojo path AA that can do the right
     // thing WRT to the blend then we'll need some query on the PR.
-    if (disable_coverage_aa_for_blend(target)) {
-#ifdef SK_DEBUG
-        //GrPrintf("Turning off AA to correctly apply blend.\n");
-#endif
-        useAA = false;
-    }
+    bool useCoverageAA = useAA &&
+        !target->getDrawState().getRenderTarget()->isMultisampled() &&
+        !target->shouldDisableCoverageAAForBlend();
 
-    GrPathRendererChain::DrawType type = useAA ? GrPathRendererChain::kColorAntiAlias_DrawType :
-                                                 GrPathRendererChain::kColor_DrawType;
+
+    GrPathRendererChain::DrawType type =
+        useCoverageAA ? GrPathRendererChain::kColorAntiAlias_DrawType :
+                           GrPathRendererChain::kColor_DrawType;
 
     const SkPath* pathPtr = &path;
     SkPath tmpPath;
@@ -1198,16 +1179,16 @@
     GrPathRenderer* pr = this->getPathRenderer(*pathPtr, strokeRec, target, false, type);
 
     if (NULL == pr) {
-        if (!strokeRec.isHairlineStyle()) {
+        if (!GrPathRenderer::IsStrokeHairlineOrEquivalent(strokeRec, this->getMatrix(), NULL)) {
             // It didn't work the 1st time, so try again with the stroked path
             if (strokeRec.applyToPath(&tmpPath, *pathPtr)) {
                 pathPtr = &tmpPath;
                 strokeRec.setFillStyle();
+                if (pathPtr->isEmpty()) {
+                    return;
+                }
             }
         }
-        if (pathPtr->isEmpty()) {
-            return;
-        }
 
         // This time, allow SW renderer
         pr = this->getPathRenderer(*pathPtr, strokeRec, target, true, type);
@@ -1220,7 +1201,7 @@
         return;
     }
 
-    pr->drawPath(*pathPtr, strokeRec, target, useAA);
+    pr->drawPath(*pathPtr, strokeRec, target, useCoverageAA);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/GrDefaultPathRenderer.cpp b/src/gpu/GrDefaultPathRenderer.cpp
index 85485c8..1f20ac2 100644
--- a/src/gpu/GrDefaultPathRenderer.cpp
+++ b/src/gpu/GrDefaultPathRenderer.cpp
@@ -12,6 +12,7 @@
 #include "GrPathUtils.h"
 #include "SkString.h"
 #include "SkStrokeRec.h"
+#include "SkTLazy.h"
 #include "SkTrace.h"
 
 
@@ -325,11 +326,25 @@
 }
 
 bool GrDefaultPathRenderer::internalDrawPath(const SkPath& path,
-                                             const SkStrokeRec& stroke,
+                                             const SkStrokeRec& origStroke,
                                              GrDrawTarget* target,
                                              bool stencilOnly) {
 
     SkMatrix viewM = target->getDrawState().getViewMatrix();
+    SkTCopyOnFirstWrite<SkStrokeRec> stroke(origStroke);
+
+    SkScalar hairlineCoverage;
+    if (IsStrokeHairlineOrEquivalent(*stroke, target->getDrawState().getViewMatrix(),
+                                     &hairlineCoverage)) {
+        uint8_t newCoverage = SkScalarRoundToInt(hairlineCoverage *
+                                                 target->getDrawState().getCoverage());
+        target->drawState()->setCoverage(newCoverage);
+
+        if (!stroke->isHairlineStyle()) {
+            stroke.writable()->setHairlineStyle();
+        }
+    }
+
     SkScalar tol = SK_Scalar1;
     tol = GrPathUtils::scaleToleranceToSrc(tol, viewM, path.getBounds());
 
@@ -338,7 +353,7 @@
     GrPrimitiveType primType;
     GrDrawTarget::AutoReleaseGeometry arg;
     if (!this->createGeom(path,
-                          stroke,
+                          *stroke,
                           tol,
                           target,
                           &primType,
@@ -361,7 +376,7 @@
     bool                        reverse = false;
     bool                        lastPassIsBounds;
 
-    if (stroke.isHairlineStyle()) {
+    if (stroke->isHairlineStyle()) {
         passCount = 1;
         if (stencilOnly) {
             passes[0] = &gDirectToStencil;
@@ -371,7 +386,7 @@
         lastPassIsBounds = false;
         drawFace[0] = GrDrawState::kBoth_DrawFace;
     } else {
-        if (single_pass_path(path, stroke)) {
+        if (single_pass_path(path, *stroke)) {
             passCount = 1;
             if (stencilOnly) {
                 passes[0] = &gDirectToStencil;
@@ -499,7 +514,10 @@
                                         const GrDrawTarget* target,
                                         bool antiAlias) const {
     // this class can draw any path with any fill but doesn't do any anti-aliasing.
-    return (stroke.isFillStyle() || stroke.isHairlineStyle()) && !antiAlias;
+
+    return !antiAlias &&
+        (stroke.isFillStyle() ||
+         IsStrokeHairlineOrEquivalent(stroke, target->getDrawState().getViewMatrix(), NULL));
 }
 
 bool GrDefaultPathRenderer::onDrawPath(const SkPath& path,
diff --git a/src/gpu/GrDrawState.cpp b/src/gpu/GrDrawState.cpp
index 1743604..1bbcc26 100644
--- a/src/gpu/GrDrawState.cpp
+++ b/src/gpu/GrDrawState.cpp
@@ -54,7 +54,6 @@
     this->enableState(GrDrawState::kClip_StateBit);
 
     this->setColor(paint.getColor());
-    this->setCoverage4(paint.getCoverage());
     this->setState(GrDrawState::kDither_StateBit, paint.isDither());
     this->setState(GrDrawState::kHWAntialias_StateBit, paint.isAntiAlias());
 
@@ -220,7 +219,7 @@
 
     // Check whether coverage is treated as color. If so we run through the coverage computation.
     if (this->isCoverageDrawing()) {
-        GrColor coverageColor = this->getCoverage();
+        GrColor coverageColor = this->getCoverageColor();
         GrColor oldColor = color;
         color = 0;
         for (int c = 0; c < 4; ++c) {
@@ -312,7 +311,7 @@
 
     bool covIsZero = !this->isCoverageDrawing() &&
                      !this->hasCoverageVertexAttribute() &&
-                     0 == this->getCoverage();
+                     0 == this->getCoverageColor();
     // When coeffs are (0,1) there is no reason to draw at all, unless
     // stenciling is enabled. Having color writes disabled is effectively
     // (0,1). The same applies when coverage is known to be 0.
@@ -327,7 +326,7 @@
 
     // check for coverage due to constant coverage, per-vertex coverage, or coverage stage
     bool hasCoverage = forceCoverage ||
-                       0xffffffff != this->getCoverage() ||
+                       0xffffffff != this->getCoverageColor() ||
                        this->hasCoverageVertexAttribute() ||
                        fCoverageStages.count() > 0;
 
diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h
index 2e4d7f8..3de0b12 100644
--- a/src/gpu/GrDrawState.h
+++ b/src/gpu/GrDrawState.h
@@ -311,15 +311,11 @@
         fCommon.fCoverage = GrColorPackRGBA(coverage, coverage, coverage, coverage);
     }
 
-    /**
-     * Version of above that specifies 4 channel per-vertex color. The value
-     * should be premultiplied.
-     */
-    void setCoverage4(GrColor coverage) {
-        fCommon.fCoverage = coverage;
+    uint8_t getCoverage() const {
+        return GrColorUnpackR(fCommon.fCoverage);
     }
 
-    GrColor getCoverage() const {
+    GrColor getCoverageColor() const {
         return fCommon.fCoverage;
     }
 
diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h
index 266dc07..c505869 100644
--- a/src/gpu/GrDrawTarget.h
+++ b/src/gpu/GrDrawTarget.h
@@ -100,6 +100,15 @@
      */
     bool canApplyCoverage() const;
 
+    /** When we're using coverage AA but the blend is incompatible (given gpu
+     * limitations) we should disable AA. */
+    bool shouldDisableCoverageAAForBlend() {
+        // Enable below if we should draw with AA even when it produces
+        // incorrect blending.
+        // return false;
+        return !this->canApplyCoverage();
+    }
+
     /**
      * Given the current draw state and hw support, will HW AA lines be used (if
      * a line primitive type is drawn)?
diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp
index aeeb85d..8f078db 100644
--- a/src/gpu/GrOvalRenderer.cpp
+++ b/src/gpu/GrOvalRenderer.cpp
@@ -460,7 +460,11 @@
 bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, bool useAA,
                               const SkRect& oval, const SkStrokeRec& stroke)
 {
-    if (!useAA) {
+    bool useCoverageAA = useAA &&
+        !target->getDrawState().getRenderTarget()->isMultisampled() &&
+        !target->shouldDisableCoverageAAForBlend();
+
+    if (!useCoverageAA) {
         return false;
     }
 
@@ -469,13 +473,13 @@
     // we can draw circles
     if (SkScalarNearlyEqual(oval.width(), oval.height())
         && circle_stays_circle(vm)) {
-        this->drawCircle(target, useAA, oval, stroke);
+        this->drawCircle(target, useCoverageAA, oval, stroke);
     // if we have shader derivative support, render as device-independent
     } else if (target->caps()->shaderDerivativeSupport()) {
-        return this->drawDIEllipse(target, useAA, oval, stroke);
+        return this->drawDIEllipse(target, useCoverageAA, oval, stroke);
     // otherwise axis-aligned ellipses only
     } else if (vm.rectStaysRect()) {
-        return this->drawEllipse(target, useAA, oval, stroke);
+        return this->drawEllipse(target, useCoverageAA, oval, stroke);
     } else {
         return false;
     }
@@ -492,7 +496,7 @@
 };
 
 void GrOvalRenderer::drawCircle(GrDrawTarget* target,
-                                bool useAA,
+                                bool useCoverageAA,
                                 const SkRect& circle,
                                 const SkStrokeRec& stroke)
 {
@@ -597,7 +601,7 @@
 };
 
 bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
-                                 bool useAA,
+                                 bool useCoverageAA,
                                  const SkRect& ellipse,
                                  const SkStrokeRec& stroke)
 {
@@ -606,7 +610,7 @@
     {
         // we should have checked for this previously
         bool isAxisAlignedEllipse = drawState->getViewMatrix().rectStaysRect();
-        SkASSERT(useAA && isAxisAlignedEllipse);
+        SkASSERT(useCoverageAA && isAxisAlignedEllipse);
     }
 #endif
 
@@ -729,7 +733,7 @@
 }
 
 bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
-                                   bool useAA,
+                                   bool useCoverageAA,
                                    const SkRect& ellipse,
                                    const SkStrokeRec& stroke)
 {
@@ -882,8 +886,12 @@
 bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, bool useAA,
                                      const SkRRect& rrect, const SkStrokeRec& stroke)
 {
+    bool useCoverageAA = useAA &&
+        !target->getDrawState().getRenderTarget()->isMultisampled() &&
+        !target->shouldDisableCoverageAAForBlend();
+
     // only anti-aliased rrects for now
-    if (!useAA) {
+    if (!useCoverageAA) {
         return false;
     }
 
@@ -891,7 +899,7 @@
 #ifdef SK_DEBUG
     {
         // we should have checked for this previously
-        SkASSERT(useAA && vm.rectStaysRect() && rrect.isSimple());
+        SkASSERT(useCoverageAA && vm.rectStaysRect() && rrect.isSimple());
     }
 #endif
 
diff --git a/src/gpu/GrOvalRenderer.h b/src/gpu/GrOvalRenderer.h
index b58abb7..9653fcc 100644
--- a/src/gpu/GrOvalRenderer.h
+++ b/src/gpu/GrOvalRenderer.h
@@ -37,13 +37,13 @@
                          const SkRRect& rrect, const SkStrokeRec& stroke);
 
 private:
-    bool drawEllipse(GrDrawTarget* target, bool useAA,
+    bool drawEllipse(GrDrawTarget* target, bool useCoverageAA,
                      const SkRect& ellipse,
                      const SkStrokeRec& stroke);
-    bool drawDIEllipse(GrDrawTarget* target, bool useAA,
+    bool drawDIEllipse(GrDrawTarget* target, bool useCoverageAA,
                        const SkRect& ellipse,
                        const SkStrokeRec& stroke);
-    void drawCircle(GrDrawTarget* target, bool useAA,
+    void drawCircle(GrDrawTarget* target, bool useCoverageAA,
                     const SkRect& circle,
                     const SkStrokeRec& stroke);
 
diff --git a/src/gpu/GrPathRenderer.h b/src/gpu/GrPathRenderer.h
index f86eb9f..88665c1 100644
--- a/src/gpu/GrPathRenderer.h
+++ b/src/gpu/GrPathRenderer.h
@@ -13,6 +13,7 @@
 #include "GrPathRendererChain.h"
 #include "GrStencil.h"
 
+#include "SkDrawProcs.h"
 #include "SkStrokeRec.h"
 #include "SkTArray.h"
 
@@ -137,6 +138,20 @@
         this->onStencilPath(path, stroke, target);
     }
 
+    // Helper for determining if we can treat a thin stroke as a hairline w/ coverage.
+    // If we can, we draw lots faster (raster device does this same test).
+    static bool IsStrokeHairlineOrEquivalent(const SkStrokeRec& stroke, const SkMatrix& matrix,
+                                             SkScalar* outCoverage) {
+        if (stroke.isHairlineStyle()) {
+            if (NULL != outCoverage) {
+                *outCoverage = SK_Scalar1;
+            }
+            return true;
+        }
+        return stroke.getStyle() == SkStrokeRec::kStroke_Style &&
+            SkDrawTreatAAStrokeAsHairline(stroke.getWidth(), matrix, outCoverage);
+    }
+
 protected:
     /**
      * Subclass overrides if it has any limitations of stenciling support.
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 063509a..b83baf7 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -846,14 +846,6 @@
         return;
     }
 
-    // can we cheat, and treat a thin stroke as a hairline w/ coverage
-    // if we can, we draw lots faster (raster device does this same test)
-    SkScalar hairlineCoverage;
-    bool doHairLine = SkDrawTreatAsHairline(paint, fContext->getMatrix(), &hairlineCoverage);
-    if (doHairLine) {
-        grPaint.setCoverage(SkScalarRoundToInt(hairlineCoverage * grPaint.getCoverage()));
-    }
-
     // If we have a prematrix, apply it to the path, optimizing for the case
     // where the original path can in fact be modified in place (even though
     // its parameter type is const).
@@ -883,10 +875,6 @@
         pathPtr = &effectPath;
     }
 
-    if (!pathEffect && doHairLine) {
-        stroke.setHairlineStyle();
-    }
-
     if (paint.getMaskFilter()) {
         if (!stroke.isHairlineStyle()) {
             if (stroke.applyToPath(&tmpPath, *pathPtr)) {
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index accaf88..5b030fa 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -207,10 +207,10 @@
         coverage = 0;
     } else if (blendOpts & GrDrawState::kEmitCoverage_BlendOptFlag) {
         color = 0xffffffff;
-        coverage = drawState.getCoverage();
+        coverage = drawState.getCoverageColor();
     } else {
         color = drawState.getColor();
-        coverage = drawState.getCoverage();
+        coverage = drawState.getCoverageColor();
     }
 
     this->setColor(drawState, color, sharedState);
diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp
index e655210a..381461a 100644
--- a/src/gpu/gl/GrGLProgramDesc.cpp
+++ b/src/gpu/gl/GrGLProgramDesc.cpp
@@ -156,7 +156,7 @@
         header->fHasVertexCode = true;
     }
 
-    bool covIsSolidWhite = !requiresCoverageAttrib && 0xffffffff == drawState.getCoverage();
+    bool covIsSolidWhite = !requiresCoverageAttrib && 0xffffffff == drawState.getCoverageColor();
 
     if (skipCoverage) {
         header->fCoverageInput = kTransBlack_ColorInput;