Reland path bounds change with correct bounds for convex and hairline path renderers.

R=robertphillips@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@9194 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/GrAAConvexPathRenderer.cpp b/src/gpu/GrAAConvexPathRenderer.cpp
index fc6cae6..e76f0c8 100644
--- a/src/gpu/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/GrAAConvexPathRenderer.cpp
@@ -660,6 +660,24 @@
     SkSTArray<kPreallocDrawCnt, Draw, true> draws;
     create_vertices(segments, fanPt, &draws, verts, idxs);
 
+    // This is valid because all the computed verts are within 1 pixel of the path control points.
+    SkRect devBounds;
+    devBounds = origPath.getBounds();
+    adcd.getOriginalMatrix().mapRect(&devBounds);
+    devBounds.outset(SK_Scalar1, SK_Scalar1);
+
+    // Check devBounds
+#if GR_DEBUG
+    SkRect tolDevBounds = devBounds;
+    tolDevBounds.outset(SK_Scalar1 / 10000, SK_Scalar1 / 10000);
+    SkRect actualBounds;
+    actualBounds.set(verts[0].fPos, verts[1].fPos);
+    for (int i = 2; i < vCount; ++i) {
+        actualBounds.growToInclude(verts[i].fPos.fX, verts[i].fPos.fY);
+    }
+    GrAssert(tolDevBounds.contains(actualBounds));
+#endif
+
     int vOffset = 0;
     for (int i = 0; i < draws.count(); ++i) {
         const Draw& draw = draws[i];
@@ -667,7 +685,8 @@
                             vOffset,  // start vertex
                             0,        // start index
                             draw.fVertexCnt,
-                            draw.fIndexCnt);
+                            draw.fIndexCnt,
+                            &devBounds);
         vOffset += draw.fVertexCnt;
     }
 
diff --git a/src/gpu/GrAAHairLinePathRenderer.cpp b/src/gpu/GrAAHairLinePathRenderer.cpp
index da54ed6..8e3ec5b 100644
--- a/src/gpu/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/GrAAHairLinePathRenderer.cpp
@@ -360,7 +360,8 @@
 }
 
 void bloat_quad(const SkPoint qpts[3], const SkMatrix* toDevice,
-                const SkMatrix* toSrc, Vertex verts[kVertsPerQuad]) {
+                const SkMatrix* toSrc, Vertex verts[kVertsPerQuad],
+                SkRect* devBounds) {
     GrAssert(!toDevice == !toSrc);
     // original quad is specified by tri a,b,c
     SkPoint a = qpts[0];
@@ -427,7 +428,10 @@
     c1.fPos = c;
     c1.fPos -= cbN;
 
+    // This point may not be within 1 pixel of a control point. We update the bounding box to
+    // include it.
     intersect_lines(a0.fPos, abN, c0.fPos, cbN, &b0.fPos);
+    devBounds->growToInclude(b0.fPos.fX, b0.fPos.fY);
 
     if (toSrc) {
         toSrc->mapPointsWithStride(&verts[0].fPos, sizeof(Vertex), kVertsPerQuad);
@@ -439,15 +443,16 @@
                int subdiv,
                const SkMatrix* toDevice,
                const SkMatrix* toSrc,
-               Vertex** vert) {
+               Vertex** vert,
+               SkRect* devBounds) {
     GrAssert(subdiv >= 0);
     if (subdiv) {
         SkPoint newP[5];
         SkChopQuadAtHalf(p, newP);
-        add_quads(newP + 0, subdiv-1, toDevice, toSrc, vert);
-        add_quads(newP + 2, subdiv-1, toDevice, toSrc, vert);
+        add_quads(newP + 0, subdiv-1, toDevice, toSrc, vert, devBounds);
+        add_quads(newP + 2, subdiv-1, toDevice, toSrc, vert, devBounds);
     } else {
-        bloat_quad(p, toDevice, toSrc, *vert);
+        bloat_quad(p, toDevice, toSrc, *vert, devBounds);
         *vert += kVertsPerQuad;
     }
 }
@@ -704,7 +709,8 @@
             GrDrawTarget* target,
             int* lineCnt,
             int* quadCnt,
-            GrDrawTarget::AutoReleaseGeometry* arg) {
+            GrDrawTarget::AutoReleaseGeometry* arg,
+            SkRect* devBounds) {
     GrDrawState* drawState = target->drawState();
     int rtHeight = drawState->getRenderTarget()->height();
 
@@ -714,6 +720,13 @@
 
     SkMatrix viewM = drawState->getViewMatrix();
 
+    // All the vertices that we compute are within 1 of path control points with the exception of
+    // one of the bounding vertices for each quad. The add_quads() function will update the bounds
+    // for each quad added.
+    *devBounds = path.getBounds();
+    viewM.mapRect(devBounds);
+    devBounds->outset(SK_Scalar1, SK_Scalar1);
+
     PREALLOC_PTARRAY(128) lines;
     PREALLOC_PTARRAY(128) quads;
     IntArray qSubdivs;
@@ -750,7 +763,7 @@
     int unsubdivQuadCnt = quads.count() / 3;
     for (int i = 0; i < unsubdivQuadCnt; ++i) {
         GrAssert(qSubdivs[i] >= 0);
-        add_quads(&quads[3*i], qSubdivs[i], toDevice, toSrc, &verts);
+        add_quads(&quads[3*i], qSubdivs[i], toDevice, toSrc, &verts, devBounds);
     }
 
     return true;
@@ -781,11 +794,14 @@
     int lineCnt;
     int quadCnt;
     GrDrawTarget::AutoReleaseGeometry arg;
+    SkRect devBounds;
+
     if (!this->createGeom(path,
                           target,
                           &lineCnt,
                           &quadCnt,
-                          &arg)) {
+                          &arg,
+                          &devBounds)) {
         return false;
     }
 
@@ -816,6 +832,33 @@
     GrEffectRef* hairLineEffect = HairLineEdgeEffect::Create();
     GrEffectRef* hairQuadEffect = HairQuadEdgeEffect::Create();
 
+    // Check devBounds
+#if GR_DEBUG
+    SkRect tolDevBounds = devBounds;
+    tolDevBounds.outset(SK_Scalar1 / 10000, SK_Scalar1 / 10000);
+    SkRect actualBounds;
+    Vertex* verts = reinterpret_cast<Vertex*>(arg.vertices());
+    int vCount = kVertsPerLineSeg * lineCnt + kVertsPerQuad * quadCnt;
+    bool first = true;
+    for (int i = 0; i < vCount; ++i) {
+        SkPoint pos = verts[i].fPos;
+        // This is a hack to workaround the fact that we move some degenerate segments offscreen.
+        if (SK_ScalarMax == pos.fX) {
+            continue;
+        }
+        drawState->getViewMatrix().mapPoints(&pos, 1);
+        if (first) {
+            actualBounds.set(pos.fX, pos.fY, pos.fX, pos.fY);
+            first = false;
+        } else {
+            actualBounds.growToInclude(pos.fX, pos.fY);
+        }
+    }
+    if (!first) {
+        GrAssert(tolDevBounds.contains(actualBounds));
+    }
+#endif
+
     target->setIndexSourceToBuffer(fLinesIndexBuffer);
     int lines = 0;
     int nBufLines = fLinesIndexBuffer->maxQuads();
@@ -826,7 +869,8 @@
                             kVertsPerLineSeg*lines,    // startV
                             0,                         // startI
                             kVertsPerLineSeg*n,        // vCount
-                            kIdxsPerLineSeg*n);        // iCount
+                            kIdxsPerLineSeg*n,
+                            &devBounds);        // iCount
         lines += n;
     }
 
@@ -839,7 +883,8 @@
                             4 * lineCnt + kVertsPerQuad*quads, // startV
                             0,                                 // startI
                             kVertsPerQuad*n,                   // vCount
-                            kIdxsPerQuad*n);                   // iCount
+                            kIdxsPerQuad*n,                    // iCount
+                            &devBounds);
         quads += n;
     }
     target->resetIndexSource();
diff --git a/src/gpu/GrAAHairLinePathRenderer.h b/src/gpu/GrAAHairLinePathRenderer.h
index 7d1a6db..5a64905 100644
--- a/src/gpu/GrAAHairLinePathRenderer.h
+++ b/src/gpu/GrAAHairLinePathRenderer.h
@@ -29,7 +29,6 @@
                             bool antiAlias) SK_OVERRIDE;
 
 private:
-
     GrAAHairLinePathRenderer(const GrContext* context,
                              const GrIndexBuffer* fLinesIndexBuffer,
                              const GrIndexBuffer* fQuadsIndexBuffer);
@@ -38,7 +37,8 @@
                     GrDrawTarget* target,
                     int* lineCnt,
                     int* quadCnt,
-                    GrDrawTarget::AutoReleaseGeometry* arg);
+                    GrDrawTarget::AutoReleaseGeometry* arg,
+                    SkRect* devBounds   );
 
     const GrIndexBuffer*        fLinesIndexBuffer;
     const GrIndexBuffer*        fQuadsIndexBuffer;
diff --git a/src/gpu/GrDefaultPathRenderer.cpp b/src/gpu/GrDefaultPathRenderer.cpp
index 43809ed..87deb9c 100644
--- a/src/gpu/GrDefaultPathRenderer.cpp
+++ b/src/gpu/GrDefaultPathRenderer.cpp
@@ -445,7 +445,9 @@
         }
     }
 
-    {
+    SkRect devBounds;
+    GetPathDevBounds(path, drawState->getRenderTarget(), viewM, &devBounds);
+
     for (int p = 0; p < passCount; ++p) {
         drawState->setDrawFace(drawFace[p]);
         if (NULL != passes[p]) {
@@ -460,10 +462,8 @@
             GrDrawState::AutoDeviceCoordDraw adcd;
             if (reverse) {
                 GrAssert(NULL != drawState->getRenderTarget());
-                // draw over the whole world.
-                bounds.setLTRB(0, 0,
-                               SkIntToScalar(drawState->getRenderTarget()->width()),
-                               SkIntToScalar(drawState->getRenderTarget()->height()));
+                // draw over the dev bounds (which will be the whole dst surface for inv fill).
+                bounds = devBounds;
                 SkMatrix vmi;
                 // mapRect through persp matrix may not be correct
                 if (!drawState->getViewMatrix().hasPerspective() &&
@@ -483,13 +483,12 @@
             }
             if (indexCnt) {
                 target->drawIndexed(primType, 0, 0,
-                                    vertexCnt, indexCnt);
+                                    vertexCnt, indexCnt, &devBounds);
             } else {
-                target->drawNonIndexed(primType, 0, vertexCnt);
+                target->drawNonIndexed(primType, 0, vertexCnt, &devBounds);
             }
         }
     }
-    }
     return true;
 }
 
diff --git a/src/gpu/GrPathRenderer.cpp b/src/gpu/GrPathRenderer.cpp
index e0d2682..e88db22 100644
--- a/src/gpu/GrPathRenderer.cpp
+++ b/src/gpu/GrPathRenderer.cpp
@@ -12,3 +12,15 @@
 
 GrPathRenderer::GrPathRenderer() {
 }
+
+void GrPathRenderer::GetPathDevBounds(const SkPath& path,
+                                      int devW, int devH,
+                                      const SkMatrix& matrix,
+                                      SkRect* bounds) {
+    if (path.isInverseFillType()) {
+        *bounds = SkRect::MakeWH(SkIntToScalar(devW), SkIntToScalar(devH));
+        return;
+    }
+    *bounds = path.getBounds();
+    matrix.mapRect(bounds);
+}
diff --git a/src/gpu/GrPathRenderer.h b/src/gpu/GrPathRenderer.h
index ebd464f..3d4c993 100644
--- a/src/gpu/GrPathRenderer.h
+++ b/src/gpu/GrPathRenderer.h
@@ -1,4 +1,4 @@
-
+  
 /*
  * Copyright 2011 Google Inc.
  *
@@ -173,6 +173,22 @@
         this->drawPath(path, stroke, target, false);
     }
 
+    // Helper for getting the device bounds of a path. Inverse filled paths will have bounds set
+    // by devSize. Non-inverse path bounds will not necessarily be clipped to devSize.
+    static void GetPathDevBounds(const SkPath& path,
+                                 int devW,
+                                 int devH,
+                                 const SkMatrix& matrix,
+                                 SkRect* bounds);
+
+    // Helper version that gets the dev width and height from a GrSurface.
+    static void GetPathDevBounds(const SkPath& path,
+                                 const GrSurface* device,
+                                 const SkMatrix& matrix,
+                                 SkRect* bounds) {
+        GetPathDevBounds(path, device->width(), device->height(), matrix, bounds);
+    }
+
 private:
 
     typedef GrRefCnt INHERITED;