Add special handling of rectori case for gpu

https://codereview.chromium.org/15080010/



git-svn-id: http://skia.googlecode.com/svn/trunk@9175 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/GrAARectRenderer.cpp b/src/gpu/GrAARectRenderer.cpp
index c1870dd..bef5ddb 100644
--- a/src/gpu/GrAARectRenderer.cpp
+++ b/src/gpu/GrAARectRenderer.cpp
@@ -635,9 +635,16 @@
                                     const GrRect& rect,
                                     const SkMatrix& combinedMatrix,
                                     const GrRect& devRect,
-                                    const GrVec& devStrokeSize,
+                                    SkScalar width,
                                     bool useVertexCoverage) {
-    GrDrawState* drawState = target->drawState();
+    GrVec devStrokeSize;
+    if (width > 0) {
+        devStrokeSize.set(width, width);
+        combinedMatrix.mapVectors(&devStrokeSize, 1);
+        devStrokeSize.setAbs(devStrokeSize);
+    } else {
+        devStrokeSize.set(SK_Scalar1, SK_Scalar1);
+    }
 
     const SkScalar dx = devStrokeSize.fX;
     const SkScalar dy = devStrokeSize.fY;
@@ -659,13 +666,28 @@
         spare = GrMin(w, h);
     }
 
+    GrRect devOutside(devRect);
+    devOutside.outset(rx, ry);
+
     if (spare <= 0) {
-        GrRect r(devRect);
-        r.outset(rx, ry);
-        this->fillAARect(gpu, target, r, SkMatrix::I(), r, useVertexCoverage);
+        this->fillAARect(gpu, target, devOutside, SkMatrix::I(), 
+                         devOutside, useVertexCoverage);
         return;
     }
 
+    SkRect devInside(devRect);
+    devInside.inset(rx, ry);
+
+    this->geometryStrokeAARect(gpu, target, devOutside, devInside, useVertexCoverage);
+}
+
+void GrAARectRenderer::geometryStrokeAARect(GrGpu* gpu,
+                                            GrDrawTarget* target,
+                                            const SkRect& devOutside,
+                                            const SkRect& devInside,
+                                            bool useVertexCoverage) {
+    GrDrawState* drawState = target->drawState();
+
     set_aa_rect_vertex_attributes(drawState, useVertexCoverage);
 
     GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0);
@@ -691,14 +713,12 @@
     GrPoint* fan2Pos = reinterpret_cast<GrPoint*>(verts + 8 * vsize);
     GrPoint* fan3Pos = reinterpret_cast<GrPoint*>(verts + 12 * vsize);
 
-    set_inset_fan(fan0Pos, vsize, devRect,
-                  -rx - SK_ScalarHalf, -ry - SK_ScalarHalf);
-    set_inset_fan(fan1Pos, vsize, devRect,
-                  -rx + SK_ScalarHalf, -ry + SK_ScalarHalf);
-    set_inset_fan(fan2Pos, vsize, devRect,
-                  rx - SK_ScalarHalf,  ry - SK_ScalarHalf);
-    set_inset_fan(fan3Pos, vsize, devRect,
-                  rx + SK_ScalarHalf,  ry + SK_ScalarHalf);
+    // outermost
+    set_inset_fan(fan0Pos, vsize, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
+    set_inset_fan(fan1Pos, vsize, devOutside,  SK_ScalarHalf,  SK_ScalarHalf);
+    set_inset_fan(fan2Pos, vsize, devInside,  -SK_ScalarHalf, -SK_ScalarHalf);
+    // innermost
+    set_inset_fan(fan3Pos, vsize, devInside,   SK_ScalarHalf,  SK_ScalarHalf);
 
     // The outermost rect has 0 coverage
     verts += sizeof(GrPoint);
@@ -718,7 +738,7 @@
         *reinterpret_cast<GrColor*>(verts + i * vsize) = innerColor;
     }
 
-    // The innermost rect has full coverage
+    // The innermost rect has 0 coverage
     verts += 8 * vsize;
     for (int i = 0; i < 4; ++i) {
         *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
@@ -728,3 +748,24 @@
     target->drawIndexed(kTriangles_GrPrimitiveType,
                         0, 0, 16, aaStrokeRectIndexCount());
 }
+
+void GrAARectRenderer::fillAANestedRects(GrGpu* gpu,
+                                         GrDrawTarget* target,
+                                         const SkRect rects[2],
+                                         const SkMatrix& combinedMatrix,
+                                         bool useVertexCoverage) {
+    SkASSERT(combinedMatrix.rectStaysRect());
+    SkASSERT(!rects[1].isEmpty());
+
+    SkRect devOutside, devInside;
+    combinedMatrix.mapRect(&devOutside, rects[0]);
+    // can't call mapRect for devInside since it calls sort
+    combinedMatrix.mapPoints((SkPoint*)&devInside, (const SkPoint*)&rects[1], 2);
+
+    if (devInside.isEmpty()) {
+        this->fillAARect(gpu, target, devOutside, SkMatrix::I(), devOutside, useVertexCoverage);
+        return;
+    }
+
+    this->geometryStrokeAARect(gpu, target, devOutside, devInside, useVertexCoverage);
+}
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 64893b4..d25709f 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -792,17 +792,9 @@
             return;
         }
         if (width >= 0) {
-            GrVec strokeSize;
-            if (width > 0) {
-                strokeSize.set(width, width);
-                combinedMatrix.mapVectors(&strokeSize, 1);
-                strokeSize.setAbs(strokeSize);
-            } else {
-                strokeSize.set(SK_Scalar1, SK_Scalar1);
-            }
             fAARectRenderer->strokeAARect(this->getGpu(), target,
                                           rect, combinedMatrix, devRect,
-                                          strokeSize, useVertexCoverage);
+                                          width, useVertexCoverage);
         } else {
             // filled AA rect
             fAARectRenderer->fillAARect(this->getGpu(), target,
@@ -1004,6 +996,52 @@
     }
 }
 
+namespace {
+
+// Can 'path' be drawn as a pair of filled nested rectangles?
+static bool is_nested_rects(GrDrawTarget* target,
+                            const SkPath& path,
+                            const SkStrokeRec& stroke,
+                            SkRect rects[2],
+                            bool* useVertexCoverage) {
+    SkASSERT(stroke.isFillStyle());
+
+    if (path.isInverseFillType()) {
+        return false;
+    }
+
+    const GrDrawState& drawState = target->getDrawState();
+
+    // TODO: this restriction could be lifted if we were willing to apply
+    // the matrix to all the points individually rather than just to the rect
+    if (!drawState.getViewMatrix().preservesAxisAlignment()) {
+        return false;
+    }
+
+    *useVertexCoverage = false;
+    if (!target->getDrawState().canTweakAlphaForCoverage()) {
+        if (disable_coverage_aa_for_blend(target)) {
+            return false;
+        } else {
+            *useVertexCoverage = true;
+        }
+    }
+
+    SkPath::Direction dirs[2];
+    if (!path.isNestedRects(rects, dirs)) {
+        return false;
+    }
+
+    if (SkPath::kWinding_FillType == path.getFillType()) {
+        // The two rects need to be wound opposite to each other
+        return dirs[0] != dirs[1];
+    } else {
+        return true;
+    }
+}
+
+};
+
 void GrContext::drawPath(const GrPaint& paint, const SkPath& path, const SkStrokeRec& stroke) {
 
     if (path.isEmpty()) {
@@ -1021,9 +1059,28 @@
     GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW);
     GrDrawState::AutoStageDisable atr(fDrawState);
 
+    bool useAA = paint.isAntiAlias() && !this->getRenderTarget()->isMultisampled();
+    if (useAA && 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)) {
+            GrDrawState::AutoDeviceCoordDraw adcd(target->drawState());
+            if (!adcd.succeeded()) {
+                return;
+            }
+
+            fAARectRenderer->fillAANestedRects(this->getGpu(), target,
+                                               rects,
+                                               adcd.getOriginalMatrix(),
+                                               useVertexCoverage);
+            return;
+        }
+    }
+
     SkRect ovalRect;
     bool isOval = path.isOval(&ovalRect);
-    bool useAA = paint.isAntiAlias() && !this->getRenderTarget()->isMultisampled();
 
     if (!isOval || path.isInverseFillType()
         || !fOvalRenderer->drawOval(target, this, useAA, ovalRect, stroke)) {