Restrict query bounds for reduce clip to dev bounds

Review URL: https://codereview.chromium.org/1467253002
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index 9b78cdf..fa9d189 100644
--- a/src/gpu/GrClipMaskManager.cpp
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -317,8 +317,18 @@
         }
         case GrClip::kClipStack_ClipType: {
             clipSpaceRTIBounds.offset(clip.origin());
+            SkIRect clipSpaceReduceQueryBounds;
+            if (devBounds) {
+                SkIRect devIBounds = devBounds->roundOut();
+                devIBounds.offset(clip.origin());
+                if (!clipSpaceReduceQueryBounds.intersect(clipSpaceRTIBounds, devIBounds)) {
+                    return false;
+                }
+            } else {
+                clipSpaceReduceQueryBounds = clipSpaceRTIBounds;
+            }
             GrReducedClip::ReduceClipStack(*clip.clipStack(),
-                                            clipSpaceRTIBounds,
+                                            clipSpaceReduceQueryBounds,
                                             &elements,
                                             &genID,
                                             &initialState,
diff --git a/src/gpu/GrDrawContext.cpp b/src/gpu/GrDrawContext.cpp
index 6718699..1137c57 100644
--- a/src/gpu/GrDrawContext.cpp
+++ b/src/gpu/GrDrawContext.cpp
@@ -409,8 +409,10 @@
     viewMatrix.mapRect(&bounds);
 
     // If we don't have AA then we outset for a half pixel in each direction to account for
-    // snapping
-    if (!paint.isAntiAlias()) {
+    // snapping. We also do this for the "hair" primitive types: lines and points since they have
+    // a 1 pixel thickness in device space.
+    if (!paint.isAntiAlias() || GrIsPrimTypeLines(primitiveType) ||
+        kPoints_GrPrimitiveType == primitiveType) {
         bounds.outset(0.5f, 0.5f);
     }
 
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index f2ed17c..062a42f 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -508,6 +508,8 @@
                 return;
             }
             // Stop going backwards if we would cause a painter's order violation.
+            // TODO: The bounds used here do not fully consider the clip. It may be advantageous
+            // to clip each batch's bounds to the clip.
             if (intersect(candidate->bounds(), batch->bounds())) {
                 GrBATCH_INFO("\t\tIntersects with (%s, B%u)\n", candidate->name(),
                     candidate->uniqueID());
diff --git a/src/gpu/GrReducedClip.cpp b/src/gpu/GrReducedClip.cpp
index 9a6a614..cbfd896 100644
--- a/src/gpu/GrReducedClip.cpp
+++ b/src/gpu/GrReducedClip.cpp
@@ -416,10 +416,9 @@
         } else {
             if (stackBounds.contains(scalarQueryBounds)) {
                 *initialState = kAllOut_InitialState;
-                if (requiresAA) {
-                   *requiresAA = false;
-                }
-                return;
+                // We know that the bounding box contains all the pixels that are outside the clip,
+                // but we don't know that *all* the pixels in the box are outside the clip. So
+                // proceed to walking the stack.
             }
             if (tighterBounds) {
                 *tighterBounds = queryBounds;
diff --git a/src/gpu/batches/GrAALinearizingConvexPathRenderer.cpp b/src/gpu/batches/GrAALinearizingConvexPathRenderer.cpp
index e6cf035..659f9d4 100644
--- a/src/gpu/batches/GrAALinearizingConvexPathRenderer.cpp
+++ b/src/gpu/batches/GrAALinearizingConvexPathRenderer.cpp
@@ -264,6 +264,15 @@
 
         // compute bounds
         fBounds = geometry.fPath.getBounds();
+        SkScalar w = geometry.fStrokeWidth;
+        if (w > 0) {
+            w /= 2;
+            // If the miter limit is < 1 then we effectively fallback to bevel joins.
+            if (SkPaint::kMiter_Join == geometry.fJoin && w > 1.f) {
+                w *= geometry.fMiterLimit;
+            }
+            fBounds.outset(w, w);
+        }
         geometry.fViewMatrix.mapRect(&fBounds);
     }
 
diff --git a/src/gpu/batches/GrBatch.h b/src/gpu/batches/GrBatch.h
index c5fc80c..03e396a 100644
--- a/src/gpu/batches/GrBatch.h
+++ b/src/gpu/batches/GrBatch.h
@@ -31,6 +31,10 @@
  * If there are any possible optimizations which might require knowing more about the full state of
  * the draw, ie whether or not the GrBatch is allowed to tweak alpha for coverage, then this
  * information will be communicated to the GrBatch prior to geometry generation.
+ *
+ * The bounds of the batch must contain all the vertices in device space *irrespective* of the clip.
+ * The bounds are used in determining which clip elements must be applied and thus the bounds cannot
+ * in turn depend upon the clip.
  */
 #define GR_BATCH_SPEW 0
 #if GR_BATCH_SPEW
diff --git a/src/gpu/batches/GrTessellatingPathRenderer.cpp b/src/gpu/batches/GrTessellatingPathRenderer.cpp
index 901d383..a3a8883 100644
--- a/src/gpu/batches/GrTessellatingPathRenderer.cpp
+++ b/src/gpu/batches/GrTessellatingPathRenderer.cpp
@@ -1592,9 +1592,17 @@
       , fColor(color)
       , fPath(path)
       , fStroke(stroke)
-      , fViewMatrix(viewMatrix)
-      , fClipBounds(clipBounds) {
-        fBounds = path.getBounds();
+      , fViewMatrix(viewMatrix) {
+        const SkRect& pathBounds = path.getBounds();
+        fClipBounds = clipBounds;
+        // Because the clip bounds are used to add a contour for inverse fills, they must also
+        // include the path bounds.
+        fClipBounds.join(pathBounds);
+        if (path.isInverseFillType()) {
+            fBounds = fClipBounds;
+        } else {
+            fBounds = path.getBounds();
+        }
         if (!stroke.isFillStyle()) {
             SkScalar radius = SkScalarHalf(stroke.getWidth());
             if (stroke.getJoin() == SkPaint::kMiter_Join) {