Move StrokeRectBatches into .cpp files

BUG=skia:

Review URL: https://codereview.chromium.org/1353683003
diff --git a/src/gpu/GrBatchTest.cpp b/src/gpu/GrBatchTest.cpp
index 1fc2ce8..344464d 100644
--- a/src/gpu/GrBatchTest.cpp
+++ b/src/gpu/GrBatchTest.cpp
@@ -23,7 +23,7 @@
 DRAW_BATCH_TEST_EXTERN(DIEllipseBatch);
 DRAW_BATCH_TEST_EXTERN(EllipseBatch);
 DRAW_BATCH_TEST_EXTERN(GrDrawAtlasBatch);
-DRAW_BATCH_TEST_EXTERN(GrStrokeRectBatch);
+DRAW_BATCH_TEST_EXTERN(NonAAStrokeRectBatch);
 DRAW_BATCH_TEST_EXTERN(RRectBatch);
 DRAW_BATCH_TEST_EXTERN(TesselatingPathBatch);
 DRAW_BATCH_TEST_EXTERN(TextBlobBatch);
@@ -42,7 +42,7 @@
     DRAW_BATCH_TEST_ENTRY(DIEllipseBatch),
     DRAW_BATCH_TEST_ENTRY(EllipseBatch),
     DRAW_BATCH_TEST_ENTRY(GrDrawAtlasBatch),
-    DRAW_BATCH_TEST_ENTRY(GrStrokeRectBatch),
+    DRAW_BATCH_TEST_ENTRY(NonAAStrokeRectBatch),
     DRAW_BATCH_TEST_ENTRY(RRectBatch),
     DRAW_BATCH_TEST_ENTRY(TesselatingPathBatch),
     DRAW_BATCH_TEST_ENTRY(TextBlobBatch),
diff --git a/src/gpu/batches/GrAAStrokeRectBatch.cpp b/src/gpu/batches/GrAAStrokeRectBatch.cpp
index 1a55a07..4472b01 100644
--- a/src/gpu/batches/GrAAStrokeRectBatch.cpp
+++ b/src/gpu/batches/GrAAStrokeRectBatch.cpp
@@ -43,7 +43,111 @@
     return CreateForDeviceSpace(color, coverage, localCoords, viewMatrix);
 }
 
-void GrAAStrokeRectBatch::initBatchTracker(const GrPipelineOptimizations& opt) {
+class AAStrokeRectBatch : public GrVertexBatch {
+public:
+    DEFINE_BATCH_CLASS_ID
+
+    // TODO support AA rotated stroke rects by copying around view matrices
+    struct Geometry {
+        SkRect fDevOutside;
+        SkRect fDevOutsideAssist;
+        SkRect fDevInside;
+        GrColor fColor;
+        bool fMiterStroke;
+    };
+
+    static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& devOutside,
+                               const SkRect& devOutsideAssist, const SkRect& devInside,
+                               bool miterStroke) {
+        return new AAStrokeRectBatch(color, viewMatrix, devOutside, devOutsideAssist, devInside,
+                                     miterStroke);
+    }
+
+    const char* name() const override { return "AAStrokeRect"; }
+
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
+        // When this is called on a batch, there is only one geometry bundle
+        out->setKnownFourComponents(fGeoData[0].fColor);
+    }
+
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
+        out->setUnknownSingleComponent();
+    }
+
+    SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
+
+private:
+    void onPrepareDraws(Target*) override;
+    void initBatchTracker(const GrPipelineOptimizations&) override;
+
+    AAStrokeRectBatch(GrColor color, const SkMatrix& viewMatrix, const SkRect& devOutside,
+                      const SkRect& devOutsideAssist, const SkRect& devInside, bool miterStroke)
+        : INHERITED(ClassID()) {
+        fBatch.fViewMatrix = viewMatrix;
+        Geometry& geometry = fGeoData.push_back();
+        geometry.fColor = color;
+        geometry.fDevOutside = devOutside;
+        geometry.fDevOutsideAssist = devOutsideAssist;
+        geometry.fDevInside = devInside;
+        geometry.fMiterStroke = miterStroke;
+
+        // If we have miterstroke then we inset devOutside and outset devOutsideAssist, so we need
+        // the join for proper bounds
+        fBounds = geometry.fDevOutside;
+        fBounds.join(geometry.fDevOutsideAssist);
+    }
+
+
+    static const int kMiterIndexCnt = 3 * 24;
+    static const int kMiterVertexCnt = 16;
+    static const int kNumMiterRectsInIndexBuffer = 256;
+
+    static const int kBevelIndexCnt = 48 + 36 + 24;
+    static const int kBevelVertexCnt = 24;
+    static const int kNumBevelRectsInIndexBuffer = 256;
+
+    static const GrIndexBuffer* GetIndexBuffer(GrResourceProvider* resourceProvider,
+                                               bool miterStroke);
+
+    GrColor color() const { return fBatch.fColor; }
+    bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
+    bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
+    bool colorIgnored() const { return fBatch.fColorIgnored; }
+    const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
+    bool miterStroke() const { return fBatch.fMiterStroke; }
+    bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
+
+    bool onCombineIfPossible(GrBatch* t, const GrCaps&) override;
+
+    void generateAAStrokeRectGeometry(void* vertices,
+                                      size_t offset,
+                                      size_t vertexStride,
+                                      int outerVertexNum,
+                                      int innerVertexNum,
+                                      GrColor color,
+                                      const SkRect& devOutside,
+                                      const SkRect& devOutsideAssist,
+                                      const SkRect& devInside,
+                                      bool miterStroke,
+                                      bool tweakAlphaForCoverage) const;
+
+    struct BatchTracker {
+        SkMatrix fViewMatrix;
+        GrColor fColor;
+        bool fUsesLocalCoords;
+        bool fColorIgnored;
+        bool fCoverageIgnored;
+        bool fMiterStroke;
+        bool fCanTweakAlphaForCoverage;
+    };
+
+    BatchTracker fBatch;
+    SkSTArray<1, Geometry, true> fGeoData;
+
+    typedef GrVertexBatch INHERITED;
+};
+
+void AAStrokeRectBatch::initBatchTracker(const GrPipelineOptimizations& opt) {
     // Handle any color overrides
     if (!opt.readsColor()) {
         fGeoData[0].fColor = GrColor_ILLEGAL;
@@ -59,7 +163,7 @@
     fBatch.fCanTweakAlphaForCoverage = opt.canTweakAlphaForCoverage();
 }
 
-void GrAAStrokeRectBatch::onPrepareDraws(Target* target) {
+void AAStrokeRectBatch::onPrepareDraws(Target* target) {
     bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
 
     SkAutoTUnref<const GrGeometryProcessor> gp(create_stroke_rect_gp(canTweakAlphaForCoverage,
@@ -112,8 +216,8 @@
     helper.recordDraw(target);
 }
 
-const GrIndexBuffer* GrAAStrokeRectBatch::GetIndexBuffer(GrResourceProvider* resourceProvider,
-                                                         bool miterStroke) {
+const GrIndexBuffer* AAStrokeRectBatch::GetIndexBuffer(GrResourceProvider* resourceProvider,
+                                                       bool miterStroke) {
 
     if (miterStroke) {
         static const uint16_t gMiterIndices[] = {
@@ -203,8 +307,8 @@
     }
 }
 
-bool GrAAStrokeRectBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) {
-    GrAAStrokeRectBatch* that = t->cast<GrAAStrokeRectBatch>();
+bool AAStrokeRectBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) {
+    AAStrokeRectBatch* that = t->cast<AAStrokeRectBatch>();
 
     if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
                                 that->bounds(), caps)) {
@@ -237,17 +341,17 @@
     return true;
 }
 
-void GrAAStrokeRectBatch::generateAAStrokeRectGeometry(void* vertices,
-                                                       size_t offset,
-                                                       size_t vertexStride,
-                                                       int outerVertexNum,
-                                                       int innerVertexNum,
-                                                       GrColor color,
-                                                       const SkRect& devOutside,
-                                                       const SkRect& devOutsideAssist,
-                                                       const SkRect& devInside,
-                                                       bool miterStroke,
-                                                       bool tweakAlphaForCoverage) const {
+void AAStrokeRectBatch::generateAAStrokeRectGeometry(void* vertices,
+                                                     size_t offset,
+                                                     size_t vertexStride,
+                                                     int outerVertexNum,
+                                                     int innerVertexNum,
+                                                     GrColor color,
+                                                     const SkRect& devOutside,
+                                                     const SkRect& devOutsideAssist,
+                                                     const SkRect& devInside,
+                                                     bool miterStroke,
+                                                     bool tweakAlphaForCoverage) const {
     intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
 
     // We create vertices for four nested rectangles. There are two ramps from 0 to full
@@ -350,6 +454,20 @@
     }
 }
 
+namespace GrAAStrokeRectBatch {
+
+GrDrawBatch* Create(GrColor color,
+                    const SkMatrix& viewMatrix,
+                    const SkRect& devOutside,
+                    const SkRect& devOutsideAssist,
+                    const SkRect& devInside,
+                    bool miterStroke) {
+    return AAStrokeRectBatch::Create(color, viewMatrix, devOutside, devOutsideAssist, devInside,
+                                     miterStroke);
+}
+
+};
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 #ifdef GR_TEST_UTILS
@@ -368,14 +486,10 @@
     SkRect inside = outside;
     inside.inset(strokeWidth, strokeWidth);
 
-    GrAAStrokeRectBatch::Geometry geo;
-    geo.fColor = GrRandomColor(random);
-    geo.fDevOutside = outside;
-    geo.fDevOutsideAssist = outsideAssist;
-    geo.fDevInside = inside;
-    geo.fMiterStroke = miterStroke;
+    GrColor color = GrRandomColor(random);
 
-    return GrAAStrokeRectBatch::Create(geo, GrTest::TestMatrix(random));
+    return GrAAStrokeRectBatch::Create(color, GrTest::TestMatrix(random), outside, outsideAssist,
+                                       inside, miterStroke);
 }
 
 #endif
diff --git a/src/gpu/batches/GrAAStrokeRectBatch.h b/src/gpu/batches/GrAAStrokeRectBatch.h
index fad5bd9..23f160e 100644
--- a/src/gpu/batches/GrAAStrokeRectBatch.h
+++ b/src/gpu/batches/GrAAStrokeRectBatch.h
@@ -9,107 +9,21 @@
 #define GrAAStrokeRectBatch_DEFINED
 
 #include "GrColor.h"
-#include "GrTypes.h"
-#include "GrVertexBatch.h"
-#include "SkMatrix.h"
-#include "SkRect.h"
 
+class GrDrawBatch;
 class GrResourceProvider;
+class SkMatrix;
+struct SkRect;
 
-class GrAAStrokeRectBatch : public GrVertexBatch {
-public:
-    DEFINE_BATCH_CLASS_ID
+namespace GrAAStrokeRectBatch {
 
-    // TODO support AA rotated stroke rects by copying around view matrices
-    struct Geometry {
-        GrColor fColor;
-        SkRect fDevOutside;
-        SkRect fDevOutsideAssist;
-        SkRect fDevInside;
-        bool fMiterStroke;
-    };
+GrDrawBatch* Create(GrColor color,
+                    const SkMatrix& viewMatrix,
+                    const SkRect& devOutside,
+                    const SkRect& devOutsideAssist,
+                    const SkRect& devInside,
+                    bool miterStroke);
 
-    static GrDrawBatch* Create(const Geometry& geometry, const SkMatrix& viewMatrix) {
-        return new GrAAStrokeRectBatch(geometry, viewMatrix);
-    }
-
-    const char* name() const override { return "AAStrokeRect"; }
-
-    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
-        // When this is called on a batch, there is only one geometry bundle
-        out->setKnownFourComponents(fGeoData[0].fColor);
-    }
-
-    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
-        out->setUnknownSingleComponent();
-    }
-
-    SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
-
-private:
-    void onPrepareDraws(Target*) override;
-    void initBatchTracker(const GrPipelineOptimizations&) override;
-
-    GrAAStrokeRectBatch(const Geometry& geometry, const SkMatrix& viewMatrix)
-        : INHERITED(ClassID()) {
-        fBatch.fViewMatrix = viewMatrix;
-        fGeoData.push_back(geometry);
-
-        // If we have miterstroke then we inset devOutside and outset devOutsideAssist, so we need
-        // the join for proper bounds
-        fBounds = geometry.fDevOutside;
-        fBounds.join(geometry.fDevOutsideAssist);
-    }
-
-
-    static const int kMiterIndexCnt = 3 * 24;
-    static const int kMiterVertexCnt = 16;
-    static const int kNumMiterRectsInIndexBuffer = 256;
-
-    static const int kBevelIndexCnt = 48 + 36 + 24;
-    static const int kBevelVertexCnt = 24;
-    static const int kNumBevelRectsInIndexBuffer = 256;
-
-    static const GrIndexBuffer* GetIndexBuffer(GrResourceProvider* resourceProvider,
-                                               bool miterStroke);
-
-    GrColor color() const { return fBatch.fColor; }
-    bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
-    bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
-    bool colorIgnored() const { return fBatch.fColorIgnored; }
-    const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
-    bool miterStroke() const { return fBatch.fMiterStroke; }
-    bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
-
-    bool onCombineIfPossible(GrBatch* t, const GrCaps&) override;
-
-    void generateAAStrokeRectGeometry(void* vertices,
-                                      size_t offset,
-                                      size_t vertexStride,
-                                      int outerVertexNum,
-                                      int innerVertexNum,
-                                      GrColor color,
-                                      const SkRect& devOutside,
-                                      const SkRect& devOutsideAssist,
-                                      const SkRect& devInside,
-                                      bool miterStroke,
-                                      bool tweakAlphaForCoverage) const;
-
-    struct BatchTracker {
-        SkMatrix fViewMatrix;
-        GrColor fColor;
-        bool fUsesLocalCoords;
-        bool fColorIgnored;
-        bool fCoverageIgnored;
-        bool fMiterStroke;
-        bool fCanTweakAlphaForCoverage;
-    };
-
-    BatchTracker fBatch;
-    SkSTArray<1, Geometry, true> fGeoData;
-
-    typedef GrVertexBatch INHERITED;
 };
 
-
 #endif
diff --git a/src/gpu/batches/GrNonAAFillRectBatch.cpp b/src/gpu/batches/GrNonAAFillRectBatch.cpp
index 6eabba6..54edb7d 100644
--- a/src/gpu/batches/GrNonAAFillRectBatch.cpp
+++ b/src/gpu/batches/GrNonAAFillRectBatch.cpp
@@ -188,54 +188,59 @@
 typedef GrTInstanceBatch<NonAAFillRectBatchPerspectiveImp> NonAAFillRectBatchPerspective;
 
 namespace GrNonAAFillRectBatch {
+
 GrDrawBatch* Create(GrColor color,
                     const SkMatrix& viewMatrix,
                     const SkRect& rect,
                     const SkRect* localRect,
                     const SkMatrix* localMatrix) {
+    SkASSERT(!viewMatrix.hasPerspective() && (!localMatrix || !localMatrix->hasPerspective()));
+    NonAAFillRectBatchSimple* batch = NonAAFillRectBatchSimple::Create();
+    NonAAFillRectBatchSimple::Geometry& geo = *batch->geometry();
 
-    /* Perspective has to be handled in a slow path for now */
-    if (viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective())) {
-        NonAAFillRectBatchPerspective* batch = NonAAFillRectBatchPerspective::Create();
-        NonAAFillRectBatchPerspective::Geometry& geo = *batch->geometry();
+    geo.fColor = color;
+    geo.fViewMatrix = viewMatrix;
+    geo.fRect = rect;
 
-        geo.fColor = color;
-        geo.fViewMatrix = viewMatrix;
-        geo.fRect = rect;
-        geo.fHasLocalRect = SkToBool(localRect);
-        geo.fHasLocalMatrix = SkToBool(localMatrix);
-        if (localMatrix) {
-            geo.fLocalMatrix = *localMatrix;
-        }
-        if (localRect) {
-            geo.fLocalRect = *localRect;
-        }
-
-        batch->init();
-        return batch;
+    if (localRect && localMatrix) {
+        geo.fLocalQuad.setFromMappedRect(*localRect, *localMatrix);
+    } else if (localRect) {
+        geo.fLocalQuad.set(*localRect);
+    } else if (localMatrix) {
+        geo.fLocalQuad.setFromMappedRect(rect, *localMatrix);
     } else {
-        // TODO bubble these up as separate calls
-        NonAAFillRectBatchSimple* batch = NonAAFillRectBatchSimple::Create();
-        NonAAFillRectBatchSimple::Geometry& geo = *batch->geometry();
-
-        geo.fColor = color;
-        geo.fViewMatrix = viewMatrix;
-        geo.fRect = rect;
-
-        if (localRect && localMatrix) {
-            geo.fLocalQuad.setFromMappedRect(*localRect, *localMatrix);
-        } else if (localRect) {
-            geo.fLocalQuad.set(*localRect);
-        } else if (localMatrix) {
-            geo.fLocalQuad.setFromMappedRect(rect, *localMatrix);
-        } else {
-            geo.fLocalQuad.set(rect);
-        }
-
-        batch->init();
-        return batch;
+        geo.fLocalQuad.set(rect);
     }
+
+    batch->init();
+    return batch;
 }
+
+GrDrawBatch* CreateWithPerspective(GrColor color,
+                                   const SkMatrix& viewMatrix,
+                                   const SkRect& rect,
+                                   const SkRect* localRect,
+                                   const SkMatrix* localMatrix) {
+    SkASSERT(viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective()));
+    NonAAFillRectBatchPerspective* batch = NonAAFillRectBatchPerspective::Create();
+    NonAAFillRectBatchPerspective::Geometry& geo = *batch->geometry();
+
+    geo.fColor = color;
+    geo.fViewMatrix = viewMatrix;
+    geo.fRect = rect;
+    geo.fHasLocalRect = SkToBool(localRect);
+    geo.fHasLocalMatrix = SkToBool(localMatrix);
+    if (localMatrix) {
+        geo.fLocalMatrix = *localMatrix;
+    }
+    if (localRect) {
+        geo.fLocalRect = *localRect;
+    }
+
+    batch->init();
+    return batch;
+}
+
 };
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/batches/GrNonAAFillRectBatch.h b/src/gpu/batches/GrNonAAFillRectBatch.h
index ac4ca23..ac28d0f 100644
--- a/src/gpu/batches/GrNonAAFillRectBatch.h
+++ b/src/gpu/batches/GrNonAAFillRectBatch.h
@@ -15,11 +15,19 @@
 struct SkRect;
 
 namespace GrNonAAFillRectBatch {
+
 GrDrawBatch* Create(GrColor color,
                     const SkMatrix& viewMatrix,
                     const SkRect& rect,
                     const SkRect* localRect,
                     const SkMatrix* localMatrix);
+
+GrDrawBatch* CreateWithPerspective(GrColor color,
+                                   const SkMatrix& viewMatrix,
+                                   const SkRect& rect,
+                                   const SkRect* localRect,
+                                   const SkMatrix* localMatrix);
+
 };
 
 #endif
diff --git a/src/gpu/batches/GrNonAAStrokeRectBatch.cpp b/src/gpu/batches/GrNonAAStrokeRectBatch.cpp
new file mode 100644
index 0000000..2c51942
--- /dev/null
+++ b/src/gpu/batches/GrNonAAStrokeRectBatch.cpp
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrNonAAStrokeRectBatch.h"
+
+#include "GrBatchTest.h"
+#include "GrBatchFlushState.h"
+#include "GrColor.h"
+#include "GrDefaultGeoProcFactory.h"
+#include "GrVertexBatch.h"
+#include "SkRandom.h"
+
+/*  create a triangle strip that strokes the specified rect. There are 8
+    unique vertices, but we repeat the last 2 to close up. Alternatively we
+    could use an indices array, and then only send 8 verts, but not sure that
+    would be faster.
+    */
+static void init_stroke_rect_strip(SkPoint verts[10], const SkRect& rect, SkScalar width) {
+    const SkScalar rad = SkScalarHalf(width);
+    // TODO we should be able to enable this assert, but we'd have to filter these draws
+    // this is a bug
+    //SkASSERT(rad < rect.width() / 2 && rad < rect.height() / 2);
+
+    verts[0].set(rect.fLeft + rad, rect.fTop + rad);
+    verts[1].set(rect.fLeft - rad, rect.fTop - rad);
+    verts[2].set(rect.fRight - rad, rect.fTop + rad);
+    verts[3].set(rect.fRight + rad, rect.fTop - rad);
+    verts[4].set(rect.fRight - rad, rect.fBottom - rad);
+    verts[5].set(rect.fRight + rad, rect.fBottom + rad);
+    verts[6].set(rect.fLeft + rad, rect.fBottom - rad);
+    verts[7].set(rect.fLeft - rad, rect.fBottom + rad);
+    verts[8] = verts[0];
+    verts[9] = verts[1];
+}
+
+class NonAAStrokeRectBatch : public GrVertexBatch {
+public:
+    DEFINE_BATCH_CLASS_ID
+
+    struct Geometry {
+        SkMatrix fViewMatrix;
+        SkRect fRect;
+        SkScalar fStrokeWidth;
+        GrColor fColor;
+    };
+
+    static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
+                               SkScalar strokeWidth, bool snapToPixelCenters) {
+        return new NonAAStrokeRectBatch(color, viewMatrix, rect, strokeWidth, snapToPixelCenters);
+    }
+
+    const char* name() const override { return "GrStrokeRectBatch"; }
+
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
+        // When this is called on a batch, there is only one geometry bundle
+        out->setKnownFourComponents(fGeoData[0].fColor);
+    }
+
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
+        out->setKnownSingleComponent(0xff);
+    }
+
+private:
+    void onPrepareDraws(Target* target) override {
+        SkAutoTUnref<const GrGeometryProcessor> gp;
+        {
+            using namespace GrDefaultGeoProcFactory;
+            Color color(this->color());
+            Coverage coverage(this->coverageIgnored() ? Coverage::kSolid_Type :
+                                                        Coverage::kNone_Type);
+            LocalCoords localCoords(this->usesLocalCoords() ? LocalCoords::kUsePosition_Type :
+                                                              LocalCoords::kUnused_Type);
+            gp.reset(GrDefaultGeoProcFactory::Create(color, coverage, localCoords,
+                                                     this->viewMatrix()));
+        }
+
+        target->initDraw(gp, this->pipeline());
+
+        size_t vertexStride = gp->getVertexStride();
+
+        SkASSERT(vertexStride == sizeof(GrDefaultGeoProcFactory::PositionAttr));
+
+        Geometry& args = fGeoData[0];
+
+        int vertexCount = kVertsPerHairlineRect;
+        if (args.fStrokeWidth > 0) {
+            vertexCount = kVertsPerStrokeRect;
+        }
+
+        const GrVertexBuffer* vertexBuffer;
+        int firstVertex;
+
+        void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer,
+                                              &firstVertex);
+
+        if (!verts) {
+            SkDebugf("Could not allocate vertices\n");
+            return;
+        }
+
+        SkPoint* vertex = reinterpret_cast<SkPoint*>(verts);
+
+        GrPrimitiveType primType;
+
+        if (args.fStrokeWidth > 0) {;
+            primType = kTriangleStrip_GrPrimitiveType;
+            args.fRect.sort();
+            init_stroke_rect_strip(vertex, args.fRect, args.fStrokeWidth);
+        } else {
+            // hairline
+            primType = kLineStrip_GrPrimitiveType;
+            vertex[0].set(args.fRect.fLeft, args.fRect.fTop);
+            vertex[1].set(args.fRect.fRight, args.fRect.fTop);
+            vertex[2].set(args.fRect.fRight, args.fRect.fBottom);
+            vertex[3].set(args.fRect.fLeft, args.fRect.fBottom);
+            vertex[4].set(args.fRect.fLeft, args.fRect.fTop);
+        }
+
+        GrVertices vertices;
+        vertices.init(primType, vertexBuffer, firstVertex, vertexCount);
+        target->draw(vertices);
+    }
+
+    void initBatchTracker(const GrPipelineOptimizations& opt) override {
+        // Handle any color overrides
+        if (!opt.readsColor()) {
+            fGeoData[0].fColor = GrColor_ILLEGAL;
+        }
+        opt.getOverrideColorIfSet(&fGeoData[0].fColor);
+
+        // setup batch properties
+        fBatch.fColorIgnored = !opt.readsColor();
+        fBatch.fColor = fGeoData[0].fColor;
+        fBatch.fUsesLocalCoords = opt.readsLocalCoords();
+        fBatch.fCoverageIgnored = !opt.readsCoverage();
+    }
+
+    NonAAStrokeRectBatch(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
+                         SkScalar strokeWidth, bool snapToPixelCenters)
+        : INHERITED(ClassID()) {
+        Geometry& geometry = fGeoData.push_back();
+        geometry.fViewMatrix = viewMatrix;
+        geometry.fRect = rect;
+        geometry.fStrokeWidth = strokeWidth;
+        geometry.fColor = color;
+
+        fBatch.fHairline = geometry.fStrokeWidth == 0;
+
+        fGeoData.push_back(geometry);
+
+        // setup bounds
+        fBounds = geometry.fRect;
+        SkScalar rad = SkScalarHalf(geometry.fStrokeWidth);
+        fBounds.outset(rad, rad);
+        geometry.fViewMatrix.mapRect(&fBounds);
+
+        // If our caller snaps to pixel centers then we have to round out the bounds
+        if (snapToPixelCenters) {
+            fBounds.roundOut();
+        }
+    }
+
+    GrColor color() const { return fBatch.fColor; }
+    bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
+    bool colorIgnored() const { return fBatch.fColorIgnored; }
+    const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
+    bool hairline() const { return fBatch.fHairline; }
+    bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
+
+    bool onCombineIfPossible(GrBatch* t, const GrCaps&) override {
+        // if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *t->pipeline(),
+        //     t->bounds(), caps)) {
+        //     return false;
+        // }
+        // GrStrokeRectBatch* that = t->cast<StrokeRectBatch>();
+
+        // NonAA stroke rects cannot batch right now
+        // TODO make these batchable
+        return false;
+    }
+
+    struct BatchTracker {
+        GrColor fColor;
+        bool fUsesLocalCoords;
+        bool fColorIgnored;
+        bool fCoverageIgnored;
+        bool fHairline;
+    };
+
+    const static int kVertsPerHairlineRect = 5;
+    const static int kVertsPerStrokeRect = 10;
+
+    BatchTracker fBatch;
+    SkSTArray<1, Geometry, true> fGeoData;
+
+    typedef GrVertexBatch INHERITED;
+};
+
+namespace GrNonAAStrokeRectBatch {
+
+GrDrawBatch* Create(GrColor color,
+                    const SkMatrix& viewMatrix,
+                    const SkRect& rect,
+                    SkScalar strokeWidth,
+                    bool snapToPixelCenters) {
+    return NonAAStrokeRectBatch::Create(color, viewMatrix, rect, strokeWidth, snapToPixelCenters);
+}
+
+};
+
+#ifdef GR_TEST_UTILS
+
+DRAW_BATCH_TEST_DEFINE(NonAAStrokeRectBatch) {
+    SkMatrix viewMatrix = GrTest::TestMatrix(random);
+    GrColor color = GrRandomColor(random);
+    SkRect rect = GrTest::TestRect(random);
+    SkScalar strokeWidth = random->nextBool() ? 0.0f : 1.0f;
+
+    return NonAAStrokeRectBatch::Create(color, viewMatrix, rect, strokeWidth, random->nextBool());
+}
+
+#endif
diff --git a/src/gpu/batches/GrNonAAStrokeRectBatch.h b/src/gpu/batches/GrNonAAStrokeRectBatch.h
new file mode 100644
index 0000000..ed26353
--- /dev/null
+++ b/src/gpu/batches/GrNonAAStrokeRectBatch.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrNonAAStrokeRectBatch_DEFINED
+#define GrNonAAStrokeRectBatch_DEFINED
+
+#include "GrColor.h"
+
+#include "SkTypes.h"
+
+class GrDrawBatch;
+struct SkRect;
+class SkMatrix;
+
+namespace GrNonAAStrokeRectBatch {
+
+GrDrawBatch* Create(GrColor color,
+                    const SkMatrix& viewMatrix,
+                    const SkRect& rect,
+                    SkScalar strokeWidth,
+                    bool snapToPixelCenters);
+
+};
+
+#endif
diff --git a/src/gpu/batches/GrRectBatchFactory.cpp b/src/gpu/batches/GrRectBatchFactory.cpp
index f01010b..eed18ad 100644
--- a/src/gpu/batches/GrRectBatchFactory.cpp
+++ b/src/gpu/batches/GrRectBatchFactory.cpp
@@ -8,41 +8,11 @@
 #include "GrRectBatchFactory.h"
 
 #include "GrAAStrokeRectBatch.h"
-#include "GrStrokeRectBatch.h"
 
 #include "SkStrokeRec.h"
 
-static GrDrawBatch* create_stroke_aa_batch(GrColor color,
-                                           const SkMatrix& viewMatrix,
-                                           const SkRect& devOutside,
-                                           const SkRect& devOutsideAssist,
-                                           const SkRect& devInside,
-                                           bool miterStroke) {
-    GrAAStrokeRectBatch::Geometry geometry;
-    geometry.fColor = color;
-    geometry.fDevOutside = devOutside;
-    geometry.fDevOutsideAssist = devOutsideAssist;
-    geometry.fDevInside = devInside;
-    geometry.fMiterStroke = miterStroke;
-
-    return GrAAStrokeRectBatch::Create(geometry, viewMatrix);
-}
-
 namespace GrRectBatchFactory {
 
-GrDrawBatch* CreateNonAAStroke(GrColor color,
-                            const SkMatrix& viewMatrix,
-                            const SkRect& rect,
-                            SkScalar strokeWidth,
-                            bool snapToPixelCenters) {
-    GrStrokeRectBatch::Geometry geometry;
-    geometry.fColor = color;
-    geometry.fViewMatrix = viewMatrix;
-    geometry.fRect = rect;
-    geometry.fStrokeWidth = strokeWidth;
-    return GrStrokeRectBatch::Create(geometry, snapToPixelCenters);
-}
-
 GrDrawBatch* CreateAAStroke(GrColor color,
                             const SkMatrix& viewMatrix,
                             const SkRect& rect,
@@ -98,8 +68,8 @@
         devOutsideAssist.outset(0, ry);
     }
 
-    return create_stroke_aa_batch(color, viewMatrix, devOutside, devOutsideAssist, devInside,
-                                  miterStroke);
+    return GrAAStrokeRectBatch::Create(color, viewMatrix, devOutside, devOutsideAssist, devInside,
+                                       miterStroke);
 }
 
 GrDrawBatch* CreateAAFillNestedRects(GrColor color,
@@ -116,7 +86,7 @@
         return CreateAAFill(color, viewMatrix, devOutside, devOutside);
     }
 
-    return create_stroke_aa_batch(color, viewMatrix, devOutside, devOutside, devInside, true);
+    return GrAAStrokeRectBatch::Create(color, viewMatrix, devOutside, devOutside, devInside, true);
 }
 
 };
diff --git a/src/gpu/batches/GrRectBatchFactory.h b/src/gpu/batches/GrRectBatchFactory.h
index 3de8c02..fbbc66a 100644
--- a/src/gpu/batches/GrRectBatchFactory.h
+++ b/src/gpu/batches/GrRectBatchFactory.h
@@ -11,9 +11,10 @@
 #include "GrAAFillRectBatch.h"
 #include "GrColor.h"
 #include "GrNonAAFillRectBatch.h"
+#include "GrNonAAStrokeRectBatch.h"
+#include "SkMatrix.h"
 
 class GrBatch;
-class SkMatrix;
 struct SkRect;
 class SkStrokeRec;
 
@@ -27,7 +28,12 @@
                                     const SkRect& rect,
                                     const SkRect* localRect,
                                     const SkMatrix* localMatrix) {
-    return GrNonAAFillRectBatch::Create(color, viewMatrix, rect, localRect, localMatrix);
+    if (viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective())) {
+        return GrNonAAFillRectBatch::CreateWithPerspective(color, viewMatrix, rect, localRect,
+                                                           localMatrix);
+    } else {
+        return GrNonAAFillRectBatch::Create(color, viewMatrix, rect, localRect, localMatrix);
+    }
 }
 
 inline GrDrawBatch* CreateAAFill(GrColor color,
@@ -45,11 +51,13 @@
     return GrAAFillRectBatch::Create(color, viewMatrix, localMatrix, rect, devRect);
 }
 
-GrDrawBatch* CreateNonAAStroke(GrColor color,
-                               const SkMatrix& viewMatrix,
-                               const SkRect& rect,
-                               SkScalar strokeWidth,
-                               bool snapToPixelCenters);
+inline GrDrawBatch* CreateNonAAStroke(GrColor color,
+                                      const SkMatrix& viewMatrix,
+                                      const SkRect& rect,
+                                      SkScalar strokeWidth,
+                                      bool snapToPixelCenters) {
+    return GrNonAAStrokeRectBatch::Create(color, viewMatrix, rect, strokeWidth, snapToPixelCenters);
+}
 
 GrDrawBatch* CreateAAStroke(GrColor,
                             const SkMatrix& viewMatrix,
diff --git a/src/gpu/batches/GrStrokeRectBatch.cpp b/src/gpu/batches/GrStrokeRectBatch.cpp
deleted file mode 100644
index 20c754c..0000000
--- a/src/gpu/batches/GrStrokeRectBatch.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright 2015 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "GrStrokeRectBatch.h"
-#include "GrBatchTest.h"
-#include "GrBatchFlushState.h"
-#include "SkRandom.h"
-
-GrStrokeRectBatch::GrStrokeRectBatch(const Geometry& geometry, bool snapToPixelCenters)
-    : INHERITED(ClassID()) {
-    fBatch.fHairline = geometry.fStrokeWidth == 0;
-
-    fGeoData.push_back(geometry);
-
-    // setup bounds
-    fBounds = geometry.fRect;
-    SkScalar rad = SkScalarHalf(geometry.fStrokeWidth);
-    fBounds.outset(rad, rad);
-    geometry.fViewMatrix.mapRect(&fBounds);
-
-    // If our caller snaps to pixel centers then we have to round out the bounds
-    if (snapToPixelCenters) {
-        fBounds.roundOut();
-    }
-}
-
-void GrStrokeRectBatch::initBatchTracker(const GrPipelineOptimizations& opt) {
-    // Handle any color overrides
-    if (!opt.readsColor()) {
-        fGeoData[0].fColor = GrColor_ILLEGAL;
-    }
-    opt.getOverrideColorIfSet(&fGeoData[0].fColor);
-
-    // setup batch properties
-    fBatch.fColorIgnored = !opt.readsColor();
-    fBatch.fColor = fGeoData[0].fColor;
-    fBatch.fUsesLocalCoords = opt.readsLocalCoords();
-    fBatch.fCoverageIgnored = !opt.readsCoverage();
-}
-
-/*  create a triangle strip that strokes the specified rect. There are 8
-    unique vertices, but we repeat the last 2 to close up. Alternatively we
-    could use an indices array, and then only send 8 verts, but not sure that
-    would be faster.
-    */
-static void init_stroke_rect_strip(SkPoint verts[10], const SkRect& rect, SkScalar width) {
-    const SkScalar rad = SkScalarHalf(width);
-    // TODO we should be able to enable this assert, but we'd have to filter these draws
-    // this is a bug
-    //SkASSERT(rad < rect.width() / 2 && rad < rect.height() / 2);
-
-    verts[0].set(rect.fLeft + rad, rect.fTop + rad);
-    verts[1].set(rect.fLeft - rad, rect.fTop - rad);
-    verts[2].set(rect.fRight - rad, rect.fTop + rad);
-    verts[3].set(rect.fRight + rad, rect.fTop - rad);
-    verts[4].set(rect.fRight - rad, rect.fBottom - rad);
-    verts[5].set(rect.fRight + rad, rect.fBottom + rad);
-    verts[6].set(rect.fLeft + rad, rect.fBottom - rad);
-    verts[7].set(rect.fLeft - rad, rect.fBottom + rad);
-    verts[8] = verts[0];
-    verts[9] = verts[1];
-}
-
-void GrStrokeRectBatch::onPrepareDraws(Target* target) {
-    SkAutoTUnref<const GrGeometryProcessor> gp;
-    {
-        using namespace GrDefaultGeoProcFactory;
-        Color color(this->color());
-        Coverage coverage(this->coverageIgnored() ? Coverage::kSolid_Type :
-                                                    Coverage::kNone_Type);
-        LocalCoords localCoords(this->usesLocalCoords() ? LocalCoords::kUsePosition_Type :
-                                                            LocalCoords::kUnused_Type);
-        gp.reset(GrDefaultGeoProcFactory::Create(color, coverage, localCoords,
-                                                    this->viewMatrix()));
-    }
-
-    target->initDraw(gp, this->pipeline());
-
-    size_t vertexStride = gp->getVertexStride();
-
-    SkASSERT(vertexStride == sizeof(GrDefaultGeoProcFactory::PositionAttr));
-
-    Geometry& args = fGeoData[0];
-
-    int vertexCount = kVertsPerHairlineRect;
-    if (args.fStrokeWidth > 0) {
-        vertexCount = kVertsPerStrokeRect;
-    }
-
-    const GrVertexBuffer* vertexBuffer;
-    int firstVertex;
-
-    void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex);
-
-    if (!verts) {
-        SkDebugf("Could not allocate vertices\n");
-        return;
-    }
-
-    SkPoint* vertex = reinterpret_cast<SkPoint*>(verts);
-
-    GrPrimitiveType primType;
-
-    if (args.fStrokeWidth > 0) {;
-        primType = kTriangleStrip_GrPrimitiveType;
-        args.fRect.sort();
-        init_stroke_rect_strip(vertex, args.fRect, args.fStrokeWidth);
-    } else {
-        // hairline
-        primType = kLineStrip_GrPrimitiveType;
-        vertex[0].set(args.fRect.fLeft, args.fRect.fTop);
-        vertex[1].set(args.fRect.fRight, args.fRect.fTop);
-        vertex[2].set(args.fRect.fRight, args.fRect.fBottom);
-        vertex[3].set(args.fRect.fLeft, args.fRect.fBottom);
-        vertex[4].set(args.fRect.fLeft, args.fRect.fTop);
-    }
-
-    GrVertices vertices;
-    vertices.init(primType, vertexBuffer, firstVertex, vertexCount);
-    target->draw(vertices);
-}
-
-#ifdef GR_TEST_UTILS
-
-DRAW_BATCH_TEST_DEFINE(GrStrokeRectBatch) {
-    GrStrokeRectBatch::Geometry geometry;
-    geometry.fViewMatrix = GrTest::TestMatrix(random);
-    geometry.fColor = GrRandomColor(random);
-    geometry.fRect = GrTest::TestRect(random);
-    geometry.fStrokeWidth = random->nextBool() ? 0.0f : 1.0f;
-
-    return GrStrokeRectBatch::Create(geometry, random->nextBool());
-}
-
-#endif
diff --git a/src/gpu/batches/GrStrokeRectBatch.h b/src/gpu/batches/GrStrokeRectBatch.h
deleted file mode 100644
index b1cb8d4..0000000
--- a/src/gpu/batches/GrStrokeRectBatch.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2015 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrStrokeRectBatch_DEFINED
-#define GrStrokeRectBatch_DEFINED
-
-#include "GrColor.h"
-#include "GrDefaultGeoProcFactory.h"
-#include "GrVertexBatch.h"
-
-class GrStrokeRectBatch : public GrVertexBatch {
-public:
-    DEFINE_BATCH_CLASS_ID
-
-    struct Geometry {
-        GrColor fColor;
-        SkMatrix fViewMatrix;
-        SkRect fRect;
-        SkScalar fStrokeWidth;
-    };
-
-    static GrDrawBatch* Create(const Geometry& geometry, bool snapToPixelCenters) {
-        return new GrStrokeRectBatch(geometry, snapToPixelCenters);
-    }
-
-    const char* name() const override { return "GrStrokeRectBatch"; }
-
-    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
-        // When this is called on a batch, there is only one geometry bundle
-        out->setKnownFourComponents(fGeoData[0].fColor);
-    }
-
-    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
-        out->setKnownSingleComponent(0xff);
-    }
-
-private:
-    void onPrepareDraws(Target*) override;
-    void initBatchTracker(const GrPipelineOptimizations&) override;
-
-    GrStrokeRectBatch(const Geometry& geometry, bool snapToPixelCenters);
-
-    GrColor color() const { return fBatch.fColor; }
-    bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
-    bool colorIgnored() const { return fBatch.fColorIgnored; }
-    const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
-    bool hairline() const { return fBatch.fHairline; }
-    bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
-
-    bool onCombineIfPossible(GrBatch* t, const GrCaps&) override {
-        // if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *t->pipeline(),
-        //     t->bounds(), caps)) {
-        //     return false;
-        // }
-        // GrStrokeRectBatch* that = t->cast<StrokeRectBatch>();
-
-        // NonAA stroke rects cannot batch right now
-        // TODO make these batchable
-        return false;
-    }
-
-    struct BatchTracker {
-        GrColor fColor;
-        bool fUsesLocalCoords;
-        bool fColorIgnored;
-        bool fCoverageIgnored;
-        bool fHairline;
-    };
-
-    const static int kVertsPerHairlineRect = 5;
-    const static int kVertsPerStrokeRect = 10;
-
-    BatchTracker fBatch;
-    SkSTArray<1, Geometry, true> fGeoData;
-
-    typedef GrVertexBatch INHERITED;
-};
-
-#endif