Converts remaining rect ops from GrLegacyMeshDrawOp to GrMeshDrawOp subclasses.

Consolidates op factory functions to a rewritten GrRectOpFactory.

Removes GrRenderTargetContext::drawNonAAFilledRect() in favor of creating and adding ops directly by the callers.

Change-Id: I57e5fc739bf4e92b4a4710c739e6d22cce82a479
Reviewed-on: https://skia-review.googlesource.com/17711
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/gm/bigrrectaaeffect.cpp b/gm/bigrrectaaeffect.cpp
index 92c3a24..e60cfdf 100644
--- a/gm/bigrrectaaeffect.cpp
+++ b/gm/bigrrectaaeffect.cpp
@@ -13,7 +13,7 @@
 #include "SkRRect.h"
 #include "effects/GrRRectEffect.h"
 #include "ops/GrDrawOp.h"
-#include "ops/GrNonAAFillRectOp.h"
+#include "ops/GrRectOpFactory.h"
 
 namespace skiagm {
 
@@ -89,8 +89,8 @@
                     bounds.offset(SkIntToScalar(x), SkIntToScalar(y));
 
                     renderTargetContext->priv().testingOnly_addDrawOp(
-                            GrNonAAFillRectOp::Make(std::move(grPaint), SkMatrix::I(), bounds,
-                                                    nullptr, nullptr, GrAAType::kNone));
+                            GrRectOpFactory::MakeNonAAFill(std::move(grPaint), SkMatrix::I(),
+                                                           bounds, GrAAType::kNone));
                 }
             canvas->restore();
             x = x + fTestOffsetX;
diff --git a/gm/constcolorprocessor.cpp b/gm/constcolorprocessor.cpp
index bbfca00..39926a9 100644
--- a/gm/constcolorprocessor.cpp
+++ b/gm/constcolorprocessor.cpp
@@ -18,7 +18,7 @@
 #include "SkGradientShader.h"
 #include "effects/GrConstColorProcessor.h"
 #include "ops/GrDrawOp.h"
-#include "ops/GrNonAAFillRectOp.h"
+#include "ops/GrRectOpFactory.h"
 
 namespace skiagm {
 /**
@@ -110,8 +110,8 @@
 
                     grPaint.addColorFragmentProcessor(std::move(fp));
                     renderTargetContext->priv().testingOnly_addDrawOp(
-                            GrNonAAFillRectOp::Make(std::move(grPaint), viewMatrix, renderRect,
-                                                    nullptr, nullptr, GrAAType::kNone));
+                            GrRectOpFactory::MakeNonAAFill(std::move(grPaint), viewMatrix,
+                                                           renderRect, GrAAType::kNone));
 
                     // Draw labels for the input to the processor and the processor to the right of
                     // the test rect. The input label appears above the processor label.
diff --git a/gm/rrects.cpp b/gm/rrects.cpp
index 23f576b..3124895 100644
--- a/gm/rrects.cpp
+++ b/gm/rrects.cpp
@@ -12,7 +12,7 @@
 #include "GrRenderTargetContextPriv.h"
 #include "effects/GrRRectEffect.h"
 #include "ops/GrDrawOp.h"
-#include "ops/GrNonAAFillRectOp.h"
+#include "ops/GrRectOpFactory.h"
 #endif
 #include "SkRRect.h"
 
@@ -117,12 +117,9 @@
                             bounds.outset(2.f, 2.f);
 
                             renderTargetContext->priv().testingOnly_addDrawOp(
-                                    GrNonAAFillRectOp::Make(std::move(grPaint),
-                                                            SkMatrix::I(),
-                                                            bounds,
-                                                            nullptr,
-                                                            nullptr,
-                                                            GrAAType::kNone));
+                                    GrRectOpFactory::MakeNonAAFill(std::move(grPaint),
+                                                                   SkMatrix::I(), bounds,
+                                                                   GrAAType::kNone));
                         } else {
                             drew = false;
                         }
diff --git a/gm/texturedomaineffect.cpp b/gm/texturedomaineffect.cpp
index d49e059..e54404e 100644
--- a/gm/texturedomaineffect.cpp
+++ b/gm/texturedomaineffect.cpp
@@ -18,7 +18,7 @@
 #include "SkGradientShader.h"
 #include "effects/GrTextureDomain.h"
 #include "ops/GrDrawOp.h"
-#include "ops/GrNonAAFillRectOp.h"
+#include "ops/GrRectOpFactory.h"
 
 namespace skiagm {
 /**
@@ -134,8 +134,8 @@
                     const SkMatrix viewMatrix = SkMatrix::MakeTrans(x, y);
                     grPaint.addColorFragmentProcessor(std::move(fp));
                     renderTargetContext->priv().testingOnly_addDrawOp(
-                            GrNonAAFillRectOp::Make(std::move(grPaint), viewMatrix, renderRect,
-                                                    nullptr, nullptr, GrAAType::kNone));
+                            GrRectOpFactory::MakeNonAAFill(std::move(grPaint), viewMatrix,
+                                                           renderRect, GrAAType::kNone));
                     x += renderRect.width() + kTestPad;
                 }
                 y += renderRect.height() + kTestPad;
diff --git a/gm/yuvtorgbeffect.cpp b/gm/yuvtorgbeffect.cpp
index eb2e422..c6effed 100644
--- a/gm/yuvtorgbeffect.cpp
+++ b/gm/yuvtorgbeffect.cpp
@@ -19,7 +19,7 @@
 #include "SkGradientShader.h"
 #include "effects/GrYUVEffect.h"
 #include "ops/GrDrawOp.h"
-#include "ops/GrNonAAFillRectOp.h"
+#include "ops/GrRectOpFactory.h"
 
 #define YSIZE 8
 #define USIZE 4
@@ -133,8 +133,8 @@
                     SkMatrix viewMatrix;
                     viewMatrix.setTranslate(x, y);
                     renderTargetContext->priv().testingOnly_addDrawOp(
-                            GrNonAAFillRectOp::Make(std::move(grPaint), viewMatrix, renderRect,
-                                                    nullptr, nullptr, GrAAType::kNone));
+                            GrRectOpFactory::MakeNonAAFill(std::move(grPaint), viewMatrix,
+                                                           renderRect, GrAAType::kNone));
                 }
                 x += renderRect.width() + kTestPad;
             }
@@ -256,9 +256,8 @@
                 SkMatrix viewMatrix;
                 viewMatrix.setTranslate(x, y);
                 grPaint.addColorFragmentProcessor(fp);
-                renderTargetContext->priv().testingOnly_addDrawOp(
-                        GrNonAAFillRectOp::Make(std::move(grPaint), viewMatrix, renderRect, nullptr,
-                                                nullptr, GrAAType::kNone));
+                renderTargetContext->priv().testingOnly_addDrawOp(GrRectOpFactory::MakeNonAAFill(
+                        std::move(grPaint), viewMatrix, renderRect, GrAAType::kNone));
             }
         }
     }
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 01d0628..2a1ed00 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -230,13 +230,11 @@
   "$_src/gpu/ops/GrAAConvexPathRenderer.cpp",
   "$_src/gpu/ops/GrAAConvexPathRenderer.h",
   "$_src/gpu/ops/GrAAFillRectOp.cpp",
-  "$_src/gpu/ops/GrAAFillRectOp.h",
   "$_src/gpu/ops/GrAAHairLinePathRenderer.cpp",
   "$_src/gpu/ops/GrAAHairLinePathRenderer.h",
   "$_src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp",
   "$_src/gpu/ops/GrAALinearizingConvexPathRenderer.h",
   "$_src/gpu/ops/GrAAStrokeRectOp.cpp",
-  "$_src/gpu/ops/GrAAStrokeRectOp.h",
   "$_src/gpu/ops/GrAnalyticRectOp.cpp",
   "$_src/gpu/ops/GrAnalyticRectOp.h",
   "$_src/gpu/ops/GrAtlasTextOp.cpp",
@@ -264,10 +262,8 @@
   "$_src/gpu/ops/GrMeshDrawOp.h",
   "$_src/gpu/ops/GrMSAAPathRenderer.cpp",
   "$_src/gpu/ops/GrMSAAPathRenderer.h",
-  "$_src/gpu/ops/GrNonAAFillRectOp.h",
   "$_src/gpu/ops/GrNonAAFillRectOp.cpp",
   "$_src/gpu/ops/GrNonAAStrokeRectOp.cpp",
-  "$_src/gpu/ops/GrNonAAStrokeRectOp.h",
   "$_src/gpu/ops/GrLatticeOp.cpp",
   "$_src/gpu/ops/GrLatticeOp.h",
   "$_src/gpu/ops/GrOp.cpp",
@@ -275,7 +271,6 @@
   "$_src/gpu/ops/GrOvalOpFactory.cpp",
   "$_src/gpu/ops/GrOvalOpFactory.h",
   "$_src/gpu/ops/GrRectOpFactory.h",
-  "$_src/gpu/ops/GrRectOpFactory.cpp",
   "$_src/gpu/ops/GrRegionOp.cpp",
   "$_src/gpu/ops/GrRegionOp.h",
   "$_src/gpu/ops/GrSemaphoreOp.cpp",
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 21a47ee..601d16b 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -34,7 +34,6 @@
 #include "ops/GrDrawOp.h"
 #include "ops/GrDrawVerticesOp.h"
 #include "ops/GrLatticeOp.h"
-#include "ops/GrNonAAFillRectOp.h"
 #include "ops/GrOp.h"
 #include "ops/GrOvalOpFactory.h"
 #include "ops/GrRectOpFactory.h"
@@ -283,10 +282,9 @@
 
         // We don't call drawRect() here to avoid the cropping to the, possibly smaller,
         // RenderTargetProxy bounds
-        fRenderTargetContext->drawNonAAFilledRect(GrNoClip(), std::move(paint), SkMatrix::I(),
-                                                  SkRect::Make(rtRect), nullptr, nullptr, nullptr,
-                                                  GrAAType::kNone);
-
+        std::unique_ptr<GrDrawOp> op = GrRectOpFactory::MakeNonAAFill(
+                std::move(paint), SkMatrix::I(), SkRect::Make(rtRect), GrAAType::kNone);
+        fRenderTargetContext->addDrawOp(GrNoClip(), std::move(op));
     } else {
         // This path doesn't handle coalescing of full screen clears b.c. it
         // has to clear the entire render target - not just the content area.
@@ -396,8 +394,9 @@
 
         AutoCheckFlush acf(this->drawingManager());
 
-        this->drawNonAAFilledRect(clip, std::move(paint), SkMatrix::I(), r, nullptr, &localMatrix,
-                                  nullptr, GrAAType::kNone);
+        std::unique_ptr<GrDrawOp> op = GrRectOpFactory::MakeNonAAFillWithLocalMatrix(
+                std::move(paint), SkMatrix::I(), localMatrix, r, GrAAType::kNone);
+        this->addDrawOp(clip, std::move(op));
     }
 }
 
@@ -406,10 +405,6 @@
            point.fY >= rect.fTop && point.fY <= rect.fBottom;
 }
 
-static bool view_matrix_ok_for_aa_fill_rect(const SkMatrix& viewMatrix) {
-    return viewMatrix.preservesRightAngles();
-}
-
 // Attempts to crop a rect and optional local rect to the clip boundaries.
 // Returns false if the draw can be skipped entirely.
 static bool crop_filled_rect(int width, int height, const GrClip& clip,
@@ -477,29 +472,17 @@
         }
     }
     GrAAType aaType = this->chooseAAType(aa, GrAllowMixedSamples::kNo);
+    std::unique_ptr<GrDrawOp> op;
     if (GrAAType::kCoverage == aaType) {
-        // The fill path can handle rotation but not skew.
-        if (view_matrix_ok_for_aa_fill_rect(viewMatrix)) {
-            SkRect devBoundRect;
-            viewMatrix.mapRect(&devBoundRect, croppedRect);
-            std::unique_ptr<GrLegacyMeshDrawOp> op =
-                    GrRectOpFactory::MakeAAFill(paint, viewMatrix, rect, croppedRect, devBoundRect);
-            if (op) {
-                GrPipelineBuilder pipelineBuilder(std::move(paint), aaType);
-                if (ss) {
-                    pipelineBuilder.setUserStencil(ss);
-                }
-                this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
-                return true;
-            }
-        }
+        op = GrRectOpFactory::MakeAAFill(std::move(paint), viewMatrix, rect, ss);
     } else {
-        this->drawNonAAFilledRect(clip, std::move(paint), viewMatrix, croppedRect, nullptr, nullptr,
-                                  ss, aaType);
-        return true;
+        op = GrRectOpFactory::MakeNonAAFill(std::move(paint), viewMatrix, croppedRect, aaType, ss);
     }
-
-    return false;
+    if (!op) {
+        return false;
+    }
+    this->addDrawOp(clip, std::move(op));
+    return true;
 }
 
 void GrRenderTargetContext::drawRect(const GrClip& clip,
@@ -589,30 +572,21 @@
                 }
         }
 
-        bool snapToPixelCenters = false;
-        std::unique_ptr<GrLegacyMeshDrawOp> op;
+        std::unique_ptr<GrDrawOp> op;
 
-        GrColor color = paint.getColor();
         GrAAType aaType = this->chooseAAType(aa, GrAllowMixedSamples::kNo);
         if (GrAAType::kCoverage == aaType) {
             // The stroke path needs the rect to remain axis aligned (no rotation or skew).
             if (viewMatrix.rectStaysRect()) {
-                op = GrRectOpFactory::MakeAAStroke(color, viewMatrix, rect, stroke);
+                op = GrRectOpFactory::MakeAAStroke(std::move(paint), viewMatrix, rect, stroke);
             }
         } else {
-            // Depending on sub-pixel coordinates and the particular GPU, we may lose a corner of
-            // hairline rects. We jam all the vertices to pixel centers to avoid this, but not
-            // when MSAA is enabled because it can cause ugly artifacts.
-            snapToPixelCenters = stroke.getStyle() == SkStrokeRec::kHairline_Style &&
-                                 GrFSAAType::kUnifiedMSAA != fRenderTargetProxy->fsaaType();
-            op = GrRectOpFactory::MakeNonAAStroke(color, viewMatrix, rect, stroke,
-                                                  snapToPixelCenters);
+            op = GrRectOpFactory::MakeNonAAStroke(std::move(paint), viewMatrix, rect, stroke,
+                                                  aaType);
         }
 
         if (op) {
-            GrPipelineBuilder pipelineBuilder(std::move(paint), aaType);
-            pipelineBuilder.setSnapVerticesToPixelCenters(snapToPixelCenters);
-            this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
+            this->addDrawOp(clip, std::move(op));
             return;
         }
     }
@@ -718,9 +692,9 @@
 
     GrPaint paint;
     paint.setXPFactory(GrDisableColorXPFactory::Get());
-
-    fRenderTargetContext->drawNonAAFilledRect(clip, std::move(paint), viewMatrix, rect, nullptr,
-                                              nullptr, ss, aaType);
+    std::unique_ptr<GrDrawOp> op =
+            GrRectOpFactory::MakeNonAAFill(std::move(paint), viewMatrix, rect, aaType, ss);
+    fRenderTargetContext->addDrawOp(clip, std::move(op));
 }
 
 bool GrRenderTargetContextPriv::drawAndStencilRect(const GrClip& clip,
@@ -782,16 +756,16 @@
 
     GrAAType aaType = this->chooseAAType(aa, GrAllowMixedSamples::kNo);
     if (GrAAType::kCoverage != aaType) {
-        this->drawNonAAFilledRect(clip, std::move(paint), viewMatrix, croppedRect,
-                                  &croppedLocalRect, nullptr, nullptr, aaType);
+        std::unique_ptr<GrDrawOp> op = GrRectOpFactory::MakeNonAAFillWithLocalRect(
+                std::move(paint), viewMatrix, croppedRect, croppedLocalRect, aaType);
+        this->addDrawOp(clip, std::move(op));
         return;
     }
 
-    if (view_matrix_ok_for_aa_fill_rect(viewMatrix)) {
-        std::unique_ptr<GrLegacyMeshDrawOp> op = GrAAFillRectOp::MakeWithLocalRect(
-                paint.getColor(), viewMatrix, croppedRect, croppedLocalRect);
-        GrPipelineBuilder pipelineBuilder(std::move(paint), aaType);
-        this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
+    std::unique_ptr<GrDrawOp> op = GrRectOpFactory::MakeAAFillWithLocalRect(
+            std::move(paint), viewMatrix, croppedRect, croppedLocalRect);
+    if (op) {
+        this->addDrawOp(clip, std::move(op));
         return;
     }
 
@@ -838,16 +812,16 @@
 
     GrAAType aaType = this->chooseAAType(aa, GrAllowMixedSamples::kNo);
     if (GrAAType::kCoverage != aaType) {
-        this->drawNonAAFilledRect(clip, std::move(paint), viewMatrix, croppedRect, nullptr,
-                                  &localMatrix, nullptr, aaType);
+        std::unique_ptr<GrDrawOp> op = GrRectOpFactory::MakeNonAAFillWithLocalMatrix(
+                std::move(paint), viewMatrix, localMatrix, croppedRect, aaType);
+        this->addDrawOp(clip, std::move(op));
         return;
     }
 
-    if (view_matrix_ok_for_aa_fill_rect(viewMatrix)) {
-        std::unique_ptr<GrLegacyMeshDrawOp> op =
-                GrAAFillRectOp::Make(paint.getColor(), viewMatrix, localMatrix, croppedRect);
-        GrPipelineBuilder pipelineBuilder(std::move(paint), aaType);
-        this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
+    std::unique_ptr<GrDrawOp> op = GrRectOpFactory::MakeAAFillWithLocalMatrix(
+            std::move(paint), viewMatrix, localMatrix, croppedRect);
+    if (op) {
+        this->addDrawOp(clip, std::move(op));
         return;
     }
 
@@ -1462,21 +1436,6 @@
     this->drawingManager()->prepareSurfaceForExternalIO(fRenderTargetProxy.get());
 }
 
-void GrRenderTargetContext::drawNonAAFilledRect(const GrClip& clip,
-                                                GrPaint&& paint,
-                                                const SkMatrix& viewMatrix,
-                                                const SkRect& rect,
-                                                const SkRect* localRect,
-                                                const SkMatrix* localMatrix,
-                                                const GrUserStencilSettings* ss,
-                                                GrAAType hwOrNoneAAType) {
-    SkASSERT(GrAAType::kCoverage != hwOrNoneAAType);
-    SkASSERT(GrAAType::kNone == hwOrNoneAAType || GrFSAAType::kNone != this->fsaaType());
-    std::unique_ptr<GrDrawOp> op = GrNonAAFillRectOp::Make(
-            std::move(paint), viewMatrix, rect, localRect, localMatrix, hwOrNoneAAType, ss);
-    this->addDrawOp(clip, std::move(op));
-}
-
 // Can 'path' be drawn as a pair of filled nested rectangles?
 static bool fills_as_nested_rects(const SkMatrix& viewMatrix, const SkPath& path, SkRect rects[2]) {
 
@@ -1550,13 +1509,13 @@
             SkRect rects[2];
 
             if (fills_as_nested_rects(viewMatrix, path, rects)) {
-                std::unique_ptr<GrLegacyMeshDrawOp> op =
-                        GrRectOpFactory::MakeAAFillNestedRects(paint.getColor(), viewMatrix, rects);
-                if (op) {
-                    GrPipelineBuilder pipelineBuilder(std::move(paint), aaType);
-                    this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
+                std::unique_ptr<GrDrawOp> op =
+                        GrRectOpFactory::MakeAAFillNestedRects(std::move(paint), viewMatrix, rects);
+                if (!op) {
+                    // A null return indicates that there is nothing to draw in this case.
+                    return;
                 }
-                return;
+                this->addDrawOp(clip, std::move(op));
             }
         }
         SkRect ovalRect;
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index 59edee5..922482c 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -400,15 +400,6 @@
                         const SkRect& rect,
                         const GrUserStencilSettings* ss);
 
-    void drawNonAAFilledRect(const GrClip&,
-                             GrPaint&&,
-                             const SkMatrix& viewMatrix,
-                             const SkRect& rect,
-                             const SkRect* localRect,
-                             const SkMatrix* localMatrix,
-                             const GrUserStencilSettings* ss,
-                             GrAAType hwOrNoneAAType);
-
     void internalDrawPath(
             const GrClip&, GrPaint&&, GrAA, const SkMatrix&, const SkPath&, const GrStyle&);
 
diff --git a/src/gpu/GrSWMaskHelper.cpp b/src/gpu/GrSWMaskHelper.cpp
index 141f357..fb66886 100644
--- a/src/gpu/GrSWMaskHelper.cpp
+++ b/src/gpu/GrSWMaskHelper.cpp
@@ -6,7 +6,6 @@
  */
 
 #include "GrSWMaskHelper.h"
-
 #include "GrCaps.h"
 #include "GrContext.h"
 #include "GrContextPriv.h"
@@ -15,11 +14,9 @@
 #include "GrShape.h"
 #include "GrSurfaceContext.h"
 #include "GrTextureProxy.h"
-#include "ops/GrDrawOp.h"
-
 #include "SkDistanceFieldGen.h"
-
-#include "ops/GrNonAAFillRectOp.h"
+#include "ops/GrDrawOp.h"
+#include "ops/GrRectOpFactory.h"
 
 /*
  * Convert a boolean operation into a transfer mode code
@@ -176,7 +173,8 @@
     paint.addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(
             resourceProvider, std::move(proxy), nullptr, maskMatrix,
             GrSamplerParams::kNone_FilterMode));
-    renderTargetContext->addDrawOp(
-            clip, GrNonAAFillRectOp::Make(std::move(paint), SkMatrix::I(), dstRect, nullptr,
-                                          &invert, GrAAType::kNone, &userStencilSettings));
+    renderTargetContext->addDrawOp(clip,
+                                   GrRectOpFactory::MakeNonAAFillWithLocalMatrix(
+                                           std::move(paint), SkMatrix::I(), invert, dstRect,
+                                           GrAAType::kNone, &userStencilSettings));
 }
diff --git a/src/gpu/GrSoftwarePathRenderer.cpp b/src/gpu/GrSoftwarePathRenderer.cpp
index f823e13..161b62b 100644
--- a/src/gpu/GrSoftwarePathRenderer.cpp
+++ b/src/gpu/GrSoftwarePathRenderer.cpp
@@ -12,7 +12,7 @@
 #include "GrPipelineBuilder.h"
 #include "GrResourceProvider.h"
 #include "GrSWMaskHelper.h"
-#include "ops/GrNonAAFillRectOp.h"
+#include "ops/GrRectOpFactory.h"
 
 ////////////////////////////////////////////////////////////////////////////////
 bool GrSoftwarePathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
@@ -76,9 +76,10 @@
                                            const SkMatrix& viewMatrix,
                                            const SkRect& rect,
                                            const SkMatrix& localMatrix) {
-    renderTargetContext->addDrawOp(
-            clip, GrNonAAFillRectOp::Make(std::move(paint), viewMatrix, rect, nullptr, &localMatrix,
-                                          GrAAType::kNone, &userStencilSettings));
+    renderTargetContext->addDrawOp(clip,
+                                   GrRectOpFactory::MakeNonAAFillWithLocalMatrix(
+                                           std::move(paint), viewMatrix, localMatrix, rect,
+                                           GrAAType::kNone, &userStencilSettings));
 }
 
 void GrSoftwarePathRenderer::DrawAroundInvPath(GrRenderTargetContext* renderTargetContext,
diff --git a/src/gpu/ops/GrAAFillRectOp.cpp b/src/gpu/ops/GrAAFillRectOp.cpp
index af5885c..478c109 100644
--- a/src/gpu/ops/GrAAFillRectOp.cpp
+++ b/src/gpu/ops/GrAAFillRectOp.cpp
@@ -5,21 +5,26 @@
  * found in the LICENSE file.
  */
 
-#include "GrAAFillRectOp.h"
-
 #include "GrColor.h"
 #include "GrDefaultGeoProcFactory.h"
 #include "GrMeshDrawOp.h"
 #include "GrOpFlushState.h"
+#include "GrRectOpFactory.h"
 #include "GrResourceKey.h"
 #include "GrResourceProvider.h"
 #include "GrTypes.h"
 #include "SkMatrix.h"
 #include "SkRect.h"
+#include "ops/GrSimpleMeshDrawOpHelper.h"
 
 GR_DECLARE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
 
-static void set_inset_fan(SkPoint* pts, size_t stride, const SkRect& r, SkScalar dx, SkScalar dy) {
+static inline bool view_matrix_ok_for_aa_fill_rect(const SkMatrix& viewMatrix) {
+    return viewMatrix.preservesRightAngles();
+}
+
+static inline void set_inset_fan(SkPoint* pts, size_t stride, const SkRect& r, SkScalar dx,
+                                 SkScalar dy) {
     pts->setRectFan(r.fLeft + dx, r.fTop + dy, r.fRight - dx, r.fBottom - dy, stride);
 }
 
@@ -153,16 +158,34 @@
     }
 }
 
-class AAFillRectOp final : public GrLegacyMeshDrawOp {
+namespace {
+
+class AAFillRectOp final : public GrMeshDrawOp {
+private:
+    using Helper = GrSimpleMeshDrawOpHelperWithStencil;
+
 public:
     DEFINE_OP_CLASS_ID
 
-    AAFillRectOp(GrColor color,
+    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint,
+                                          const SkMatrix& viewMatrix,
+                                          const SkRect& rect,
+                                          const SkRect& devRect,
+                                          const SkMatrix* localMatrix,
+                                          const GrUserStencilSettings* stencil) {
+        SkASSERT(view_matrix_ok_for_aa_fill_rect(viewMatrix));
+        return Helper::FactoryHelper<AAFillRectOp>(std::move(paint), viewMatrix, rect, devRect,
+                                                   localMatrix, stencil);
+    }
+
+    AAFillRectOp(const Helper::MakeArgs& helperArgs,
+                 GrColor color,
                  const SkMatrix& viewMatrix,
                  const SkRect& rect,
                  const SkRect& devRect,
-                 const SkMatrix* localMatrix)
-            : INHERITED(ClassID()) {
+                 const SkMatrix* localMatrix,
+                 const GrUserStencilSettings* stencil)
+            : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage, stencil) {
         if (localMatrix) {
             void* mem = fRectData.push_back_n(sizeof(RectWithLocalMatrixInfo));
             new (mem) RectWithLocalMatrixInfo(color, viewMatrix, rect, devRect, *localMatrix);
@@ -189,38 +212,29 @@
                         info->color(), rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
             info = this->next(info);
         }
-        str.append(DumpPipelineInfo(*this->pipeline()));
         return str;
     }
 
-    void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
-        GrColor color;
-        if (optimizations.getOverrideColorIfSet(&color)) {
-            this->first()->setColor(color);
-        }
-        fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
-        fNeedsLocalCoords = optimizations.readsLocalCoords();
+    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
+
+    bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
+        GrColor color = this->first()->color();
+        bool result = fHelper.xpRequiresDstTexture(
+                caps, clip, GrProcessorAnalysisCoverage::kSingleChannel, &color);
+        this->first()->setColor(color);
+        return result;
     }
 
 private:
-    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
-                                    GrProcessorAnalysisCoverage* coverage) const override {
-        color->setToConstant(this->first()->color());
-        *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
-    }
-
     void onPrepareDraws(Target* target) const override {
         using namespace GrDefaultGeoProcFactory;
 
         Color color(Color::kPremulGrColorAttribute_Type);
-        Coverage::Type coverageType;
-        if (fCanTweakAlphaForCoverage) {
-            coverageType = Coverage::kSolid_Type;
-        } else {
-            coverageType = Coverage::kAttribute_Type;
-        }
-        LocalCoords lc =
-                fNeedsLocalCoords ? LocalCoords::kHasExplicit_Type : LocalCoords::kUnused_Type;
+        Coverage::Type coverageType = fHelper.compatibleWithAlphaAsCoverage()
+                                              ? Coverage::kSolid_Type
+                                              : Coverage::kAttribute_Type;
+        LocalCoords lc = fHelper.usesLocalCoords() ? LocalCoords::kHasExplicit_Type
+                                                   : LocalCoords::kUnused_Type;
         sk_sp<GrGeometryProcessor> gp =
                 GrDefaultGeoProcFactory::Make(color, coverageType, lc, SkMatrix::I());
         if (!gp) {
@@ -245,7 +259,7 @@
         for (int i = 0; i < fRectCnt; i++) {
             intptr_t verts =
                     reinterpret_cast<intptr_t>(vertices) + i * kVertsPerAAFillRect * vertexStride;
-            if (fNeedsLocalCoords) {
+            if (fHelper.usesLocalCoords()) {
                 if (info->hasLocalMatrix()) {
                     localMatrix = &static_cast<const RectWithLocalMatrixInfo*>(info)->localMatrix();
                 } else {
@@ -253,28 +267,19 @@
                 }
             }
             generate_aa_fill_rect_geometry(verts, vertexStride, info->color(), info->viewMatrix(),
-                                           info->rect(), info->devRect(), fCanTweakAlphaForCoverage,
-                                           localMatrix);
+                                           info->rect(), info->devRect(),
+                                           fHelper.compatibleWithAlphaAsCoverage(), localMatrix);
             info = this->next(info);
         }
-        helper.recordDraw(target, gp.get(), this->pipeline());
+        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
         AAFillRectOp* that = t->cast<AAFillRectOp>();
-        if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
-                                    that->bounds(), caps)) {
+        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
             return false;
         }
 
-        SkASSERT(fNeedsLocalCoords == that->fNeedsLocalCoords);
-
-        // In the event of two ops, one who can tweak, one who cannot, we just fall back to not
-        // tweaking.
-        if (fCanTweakAlphaForCoverage && !that->fCanTweakAlphaForCoverage) {
-            fCanTweakAlphaForCoverage = false;
-        }
-
         fRectData.push_back_n(that->fRectData.count(), that->fRectData.begin());
         fRectCnt += that->fRectCnt;
         this->joinBounds(*that);
@@ -333,55 +338,63 @@
         return reinterpret_cast<const RectInfo*>(next);
     }
 
-    bool fNeedsLocalCoords;
-    bool fCanTweakAlphaForCoverage;
     SkSTArray<4 * sizeof(RectWithLocalMatrixInfo), uint8_t, true> fRectData;
+    Helper fHelper;
     int fRectCnt;
 
-    typedef GrLegacyMeshDrawOp INHERITED;
+    typedef GrMeshDrawOp INHERITED;
 };
 
-namespace GrAAFillRectOp {
+}  // anonymous namespace
 
-std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
-                                         const SkMatrix& viewMatrix,
-                                         const SkRect& rect,
-                                         const SkRect& devRect) {
-    return std::unique_ptr<GrLegacyMeshDrawOp>(
-            new AAFillRectOp(color, viewMatrix, rect, devRect, nullptr));
+namespace GrRectOpFactory {
+
+std::unique_ptr<GrDrawOp> MakeAAFill(GrPaint&& paint, const SkMatrix& viewMatrix,
+                                     const SkRect& rect, const GrUserStencilSettings* stencil) {
+    if (!view_matrix_ok_for_aa_fill_rect(viewMatrix)) {
+        return nullptr;
+    }
+    SkRect devRect;
+    if (!viewMatrix.mapRect(&devRect, rect)) {
+        return nullptr;
+    }
+    return AAFillRectOp::Make(std::move(paint), viewMatrix, rect, devRect, nullptr, stencil);
 }
 
-std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
-                                         const SkMatrix& viewMatrix,
-                                         const SkMatrix& localMatrix,
-                                         const SkRect& rect,
-                                         const SkRect& devRect) {
-    return std::unique_ptr<GrLegacyMeshDrawOp>(
-            new AAFillRectOp(color, viewMatrix, rect, devRect, &localMatrix));
+std::unique_ptr<GrDrawOp> MakeAAFillWithDevRect(GrPaint&& paint, const SkMatrix& viewMatrix,
+                                                const SkRect& rect, const SkRect& devRect) {
+    if (!view_matrix_ok_for_aa_fill_rect(viewMatrix)) {
+        return nullptr;
+    }
+    return AAFillRectOp::Make(std::move(paint), viewMatrix, rect, devRect, nullptr, nullptr);
 }
 
-std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
-                                         const SkMatrix& viewMatrix,
-                                         const SkMatrix& localMatrix,
-                                         const SkRect& rect) {
+std::unique_ptr<GrDrawOp> MakeAAFillWithLocalMatrix(GrPaint&& paint, const SkMatrix& viewMatrix,
+                                                    const SkMatrix& localMatrix,
+                                                    const SkRect& rect) {
+    if (!view_matrix_ok_for_aa_fill_rect(viewMatrix)) {
+        return nullptr;
+    }
     SkRect devRect;
     viewMatrix.mapRect(&devRect, rect);
-    return Make(color, viewMatrix, localMatrix, rect, devRect);
+    return AAFillRectOp::Make(std::move(paint), viewMatrix, rect, devRect, &localMatrix, nullptr);
 }
 
-std::unique_ptr<GrLegacyMeshDrawOp> MakeWithLocalRect(GrColor color,
-                                                      const SkMatrix& viewMatrix,
-                                                      const SkRect& rect,
-                                                      const SkRect& localRect) {
+std::unique_ptr<GrDrawOp> MakeAAFillWithLocalRect(GrPaint&& paint, const SkMatrix& viewMatrix,
+                                                  const SkRect& rect, const SkRect& localRect) {
+    if (!view_matrix_ok_for_aa_fill_rect(viewMatrix)) {
+        return nullptr;
+    }
     SkRect devRect;
     viewMatrix.mapRect(&devRect, rect);
     SkMatrix localMatrix;
     if (!localMatrix.setRectToRect(rect, localRect, SkMatrix::kFill_ScaleToFit)) {
         return nullptr;
     }
-    return Make(color, viewMatrix, localMatrix, rect, devRect);
+    return AAFillRectOp::Make(std::move(paint), viewMatrix, rect, devRect, &localMatrix, nullptr);
 }
-};
+
+}  // namespace GrRectOpFactory
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -389,21 +402,22 @@
 
 #include "GrDrawOpTest.h"
 
-GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(AAFillRectOp) {
-    GrColor color = GrRandomColor(random);
-    SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
+GR_DRAW_OP_TEST_DEFINE(AAFillRectOp) {
+    SkMatrix viewMatrix;
+    do {
+        viewMatrix = GrTest::TestMatrixInvertible(random);
+    } while (!view_matrix_ok_for_aa_fill_rect(viewMatrix));
     SkRect rect = GrTest::TestRect(random);
-    SkRect devRect = GrTest::TestRect(random);
-    return GrAAFillRectOp::Make(color, viewMatrix, rect, devRect);
-}
-
-GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(AAFillRectOpLocalMatrix) {
-    GrColor color = GrRandomColor(random);
-    SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
-    SkMatrix localMatrix = GrTest::TestMatrix(random);
-    SkRect rect = GrTest::TestRect(random);
-    SkRect devRect = GrTest::TestRect(random);
-    return GrAAFillRectOp::Make(color, viewMatrix, localMatrix, rect, devRect);
+    SkRect devRect;
+    viewMatrix.mapRect(&devRect, rect);
+    const SkMatrix* localMatrix = nullptr;
+    SkMatrix m;
+    if (random->nextBool()) {
+        m = GrTest::TestMatrix(random);
+    }
+    const GrUserStencilSettings* stencil =
+            random->nextBool() ? nullptr : GrGetRandomStencil(random, context);
+    return AAFillRectOp::Make(std::move(paint), viewMatrix, rect, devRect, localMatrix, stencil);
 }
 
 #endif
diff --git a/src/gpu/ops/GrAAFillRectOp.h b/src/gpu/ops/GrAAFillRectOp.h
deleted file mode 100644
index 7d97716..0000000
--- a/src/gpu/ops/GrAAFillRectOp.h
+++ /dev/null
@@ -1,41 +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 GrAAFillRectOp_DEFINED
-#define GrAAFillRectOp_DEFINED
-
-#include "GrColor.h"
-#include "SkRefCnt.h"
-
-class GrLegacyMeshDrawOp;
-class SkMatrix;
-struct SkRect;
-
-namespace GrAAFillRectOp {
-std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
-                                         const SkMatrix& viewMatrix,
-                                         const SkRect& rect,
-                                         const SkRect& devRect);
-
-std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
-                                         const SkMatrix& viewMatrix,
-                                         const SkMatrix& localMatrix,
-                                         const SkRect& rect);
-
-std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
-                                         const SkMatrix& viewMatrix,
-                                         const SkMatrix& localMatrix,
-                                         const SkRect& rect,
-                                         const SkRect& devRect);
-
-std::unique_ptr<GrLegacyMeshDrawOp> MakeWithLocalRect(GrColor color,
-                                                      const SkMatrix& viewMatrix,
-                                                      const SkRect& rect,
-                                                      const SkRect& localRect);
-};
-
-#endif
diff --git a/src/gpu/ops/GrAAStrokeRectOp.cpp b/src/gpu/ops/GrAAStrokeRectOp.cpp
index 7af6c3e..82b4ec1 100644
--- a/src/gpu/ops/GrAAStrokeRectOp.cpp
+++ b/src/gpu/ops/GrAAStrokeRectOp.cpp
@@ -5,12 +5,12 @@
  * found in the LICENSE file.
  */
 
-#include "GrAAStrokeRectOp.h"
-
 #include "GrDefaultGeoProcFactory.h"
 #include "GrOpFlushState.h"
+#include "GrRectOpFactory.h"
 #include "GrResourceKey.h"
 #include "GrResourceProvider.h"
+#include "GrSimpleMeshDrawOpHelper.h"
 #include "SkStrokeRec.h"
 
 GR_DECLARE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
@@ -110,13 +110,26 @@
                               viewMatrix);
 }
 
-class AAStrokeRectOp final : public GrLegacyMeshDrawOp {
+namespace {
+
+class AAStrokeRectOp final : public GrMeshDrawOp {
+private:
+    using Helper = GrSimpleMeshDrawOpHelper;
+
 public:
     DEFINE_OP_CLASS_ID
 
-    AAStrokeRectOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& devOutside,
-                   const SkRect& devInside)
-            : INHERITED(ClassID()), fViewMatrix(viewMatrix) {
+    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
+                                          const SkRect& devOutside, const SkRect& devInside) {
+        return Helper::FactoryHelper<AAStrokeRectOp>(std::move(paint), viewMatrix, devOutside,
+                                                     devInside);
+    }
+
+    AAStrokeRectOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
+                   const SkRect& devOutside, const SkRect& devInside)
+            : INHERITED(ClassID())
+            , fHelper(helperArgs, GrAAType::kCoverage)
+            , fViewMatrix(viewMatrix) {
         SkASSERT(!devOutside.isEmpty());
         SkASSERT(!devInside.isEmpty());
 
@@ -125,30 +138,35 @@
         fMiterStroke = true;
     }
 
-    static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
-                                                    const SkRect& rect, const SkStrokeRec& stroke) {
+    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
+                                          const SkRect& rect, const SkStrokeRec& stroke) {
         bool isMiter;
         if (!allowed_stroke(stroke, &isMiter)) {
             return nullptr;
         }
+        return Helper::FactoryHelper<AAStrokeRectOp>(std::move(paint), viewMatrix, rect, stroke,
+                                                     isMiter);
+    }
 
-        AAStrokeRectOp* op = new AAStrokeRectOp();
-        op->fMiterStroke = isMiter;
-        RectInfo& info = op->fRects.push_back();
+    AAStrokeRectOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
+                   const SkRect& rect, const SkStrokeRec& stroke, bool isMiter)
+            : INHERITED(ClassID())
+            , fHelper(helperArgs, GrAAType::kCoverage)
+            , fViewMatrix(viewMatrix) {
+        fMiterStroke = isMiter;
+        RectInfo& info = fRects.push_back();
         compute_rects(&info.fDevOutside, &info.fDevOutsideAssist, &info.fDevInside,
                       &info.fDegenerate, viewMatrix, rect, stroke.getWidth(), isMiter);
         info.fColor = color;
         if (isMiter) {
-            op->setBounds(info.fDevOutside, HasAABloat::kYes, IsZeroArea::kNo);
+            this->setBounds(info.fDevOutside, HasAABloat::kYes, IsZeroArea::kNo);
         } else {
             // The outer polygon of the bevel stroke is an octagon specified by the points of a
             // pair of overlapping rectangles where one is wide and the other is narrow.
             SkRect bounds = info.fDevOutside;
             bounds.joinPossiblyEmptyRect(info.fDevOutsideAssist);
-            op->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
+            this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
         }
-        op->fViewMatrix = viewMatrix;
-        return std::unique_ptr<GrLegacyMeshDrawOp>(op);
     }
 
     const char* name() const override { return "AAStrokeRect"; }
@@ -166,20 +184,18 @@
                     info.fDevOutsideAssist.fBottom, info.fDevInside.fLeft, info.fDevInside.fTop,
                     info.fDevInside.fRight, info.fDevInside.fBottom, info.fDegenerate);
         }
-        string.append(DumpPipelineInfo(*this->pipeline()));
         string.append(INHERITED::dumpInfo());
         return string;
     }
 
-private:
-    AAStrokeRectOp() : INHERITED(ClassID()) {}
+    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
 
-    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
-                                    GrProcessorAnalysisCoverage* coverage) const override {
-        color->setToConstant(fRects[0].fColor);
-        *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
+    bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
+        return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
+                                            &fRects.back().fColor);
     }
-    void applyPipelineOptimizations(const PipelineOptimizations&) override;
+
+private:
     void onPrepareDraws(Target*) const override;
 
     static const int kMiterIndexCnt = 3 * 24;
@@ -192,8 +208,6 @@
 
     static const GrBuffer* GetIndexBuffer(GrResourceProvider* resourceProvider, bool miterStroke);
 
-    bool usesLocalCoords() const { return fUsesLocalCoords; }
-    bool canTweakAlphaForCoverage() const { return fCanTweakAlphaForCoverage; }
     const SkMatrix& viewMatrix() const { return fViewMatrix; }
     bool miterStroke() const { return fMiterStroke; }
 
@@ -221,29 +235,20 @@
         bool fDegenerate;
     };
 
+    Helper fHelper;
     SkSTArray<1, RectInfo, true> fRects;
-    bool fUsesLocalCoords;
-    bool fCanTweakAlphaForCoverage;
     SkMatrix fViewMatrix;
     bool fMiterStroke;
 
-    typedef GrLegacyMeshDrawOp INHERITED;
+    typedef GrMeshDrawOp INHERITED;
 };
 
-void AAStrokeRectOp::applyPipelineOptimizations(const PipelineOptimizations& optimizations) {
-    optimizations.getOverrideColorIfSet(&fRects[0].fColor);
-
-    fUsesLocalCoords = optimizations.readsLocalCoords();
-    fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
-    fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
-}
+}  // anonymous namespace
 
 void AAStrokeRectOp::onPrepareDraws(Target* target) const {
-    bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
-
-    sk_sp<GrGeometryProcessor> gp(create_stroke_rect_gp(canTweakAlphaForCoverage,
+    sk_sp<GrGeometryProcessor> gp(create_stroke_rect_gp(fHelper.compatibleWithAlphaAsCoverage(),
                                                         this->viewMatrix(),
-                                                        this->usesLocalCoords()));
+                                                        fHelper.usesLocalCoords()));
     if (!gp) {
         SkDebugf("Couldn't create GrGeometryProcessor\n");
         return;
@@ -251,7 +256,7 @@
 
     size_t vertexStride = gp->getVertexStride();
 
-    SkASSERT(canTweakAlphaForCoverage
+    SkASSERT(fHelper.compatibleWithAlphaAsCoverage()
                      ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr)
                      : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
     int innerVertexNum = 4;
@@ -284,9 +289,9 @@
                                            info.fDevInside,
                                            fMiterStroke,
                                            info.fDegenerate,
-                                           canTweakAlphaForCoverage);
+                                           fHelper.compatibleWithAlphaAsCoverage());
     }
-    helper.recordDraw(target, gp.get(), this->pipeline());
+    helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
 }
 
 const GrBuffer* AAStrokeRectOp::GetIndexBuffer(GrResourceProvider* resourceProvider,
@@ -386,8 +391,7 @@
 bool AAStrokeRectOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
     AAStrokeRectOp* that = t->cast<AAStrokeRectOp>();
 
-    if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
-                                that->bounds(), caps)) {
+    if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
         return false;
     }
 
@@ -397,18 +401,11 @@
     }
 
     // We apply the viewmatrix to the rect points on the cpu.  However, if the pipeline uses
-    // local coords then we won't be able to combine.  We could actually upload the viewmatrix
-    // using vertex attributes in these cases, but haven't investigated that
-    if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
+    // local coords then we won't be able to combine. TODO: Upload local coords as an attribute.
+    if (fHelper.usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
         return false;
     }
 
-    // In the event of two ops, one who can tweak, one who cannot, we just fall back to not
-    // tweaking.
-    if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
-        fCanTweakAlphaForCoverage = false;
-    }
-
     fRects.push_back_n(that->fRects.count(), that->fRects.begin());
     this->joinBounds(*that);
     return true;
@@ -569,23 +566,35 @@
     }
 }
 
-namespace GrAAStrokeRectOp {
+namespace GrRectOpFactory {
 
-std::unique_ptr<GrLegacyMeshDrawOp> MakeFillBetweenRects(GrColor color,
-                                                         const SkMatrix& viewMatrix,
-                                                         const SkRect& devOutside,
-                                                         const SkRect& devInside) {
-    return std::unique_ptr<GrLegacyMeshDrawOp>(
-            new AAStrokeRectOp(color, viewMatrix, devOutside, devInside));
+std::unique_ptr<GrDrawOp> MakeAAFillNestedRects(GrPaint&& paint,
+                                                const SkMatrix& viewMatrix,
+                                                const SkRect rects[2]) {
+    SkASSERT(viewMatrix.rectStaysRect());
+    SkASSERT(!rects[0].isEmpty() && !rects[1].isEmpty());
+
+    SkRect devOutside, devInside;
+    viewMatrix.mapRect(&devOutside, rects[0]);
+    viewMatrix.mapRect(&devInside, rects[1]);
+    if (devInside.isEmpty()) {
+        if (devOutside.isEmpty()) {
+            return nullptr;
+        }
+        return MakeAAFillWithDevRect(std::move(paint), viewMatrix, rects[0], devOutside);
+    }
+
+    return AAStrokeRectOp::Make(std::move(paint), viewMatrix, devOutside, devInside);
 }
 
-std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
-                                         const SkMatrix& viewMatrix,
-                                         const SkRect& rect,
-                                         const SkStrokeRec& stroke) {
-    return AAStrokeRectOp::Make(color, viewMatrix, rect, stroke);
+std::unique_ptr<GrDrawOp> MakeAAStroke(GrPaint&& paint,
+                                       const SkMatrix& viewMatrix,
+                                       const SkRect& rect,
+                                       const SkStrokeRec& stroke) {
+    return AAStrokeRectOp::Make(std::move(paint), viewMatrix, rect, stroke);
 }
-}
+
+}  // namespace GrRectOpFactory
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -593,7 +602,7 @@
 
 #include "GrDrawOpTest.h"
 
-GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(AAStrokeRectOp) {
+GR_DRAW_OP_TEST_DEFINE(AAStrokeRectOp) {
     bool miterStroke = random->nextBool();
 
     // Create either a empty rect or a non-empty rect.
@@ -602,14 +611,12 @@
     SkScalar minDim = SkMinScalar(rect.width(), rect.height());
     SkScalar strokeWidth = random->nextUScalar1() * minDim;
 
-    GrColor color = GrRandomColor(random);
-
     SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
     rec.setStrokeStyle(strokeWidth);
     rec.setStrokeParams(SkPaint::kButt_Cap,
                         miterStroke ? SkPaint::kMiter_Join : SkPaint::kBevel_Join, 1.f);
     SkMatrix matrix = GrTest::TestMatrixRectStaysRect(random);
-    return GrAAStrokeRectOp::Make(color, matrix, rect, rec);
+    return GrRectOpFactory::MakeAAStroke(std::move(paint), matrix, rect, rec);
 }
 
 #endif
diff --git a/src/gpu/ops/GrAAStrokeRectOp.h b/src/gpu/ops/GrAAStrokeRectOp.h
deleted file mode 100644
index 074f8e0..0000000
--- a/src/gpu/ops/GrAAStrokeRectOp.h
+++ /dev/null
@@ -1,32 +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 GrAAStrokeRectOp_DEFINED
-#define GrAAStrokeRectOp_DEFINED
-
-#include "GrColor.h"
-#include "SkRefCnt.h"
-
-class GrLegacyMeshDrawOp;
-class SkMatrix;
-struct SkRect;
-class SkStrokeRec;
-
-namespace GrAAStrokeRectOp {
-
-std::unique_ptr<GrLegacyMeshDrawOp> MakeFillBetweenRects(GrColor color,
-                                                         const SkMatrix& viewMatrix,
-                                                         const SkRect& devOutside,
-                                                         const SkRect& devInside);
-
-std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
-                                         const SkMatrix& viewMatrix,
-                                         const SkRect& rect,
-                                         const SkStrokeRec& stroke);
-}
-
-#endif
diff --git a/src/gpu/ops/GrDefaultPathRenderer.cpp b/src/gpu/ops/GrDefaultPathRenderer.cpp
index bc5702b..c712a9c 100644
--- a/src/gpu/ops/GrDefaultPathRenderer.cpp
+++ b/src/gpu/ops/GrDefaultPathRenderer.cpp
@@ -6,7 +6,6 @@
  */
 
 #include "GrDefaultPathRenderer.h"
-
 #include "GrContext.h"
 #include "GrDefaultGeoProcFactory.h"
 #include "GrDrawOpTest.h"
@@ -20,9 +19,8 @@
 #include "SkStrokeRec.h"
 #include "SkTLazy.h"
 #include "SkTraceEvent.h"
-
 #include "ops/GrMeshDrawOp.h"
-#include "ops/GrNonAAFillRectOp.h"
+#include "ops/GrRectOpFactory.h"
 
 GrDefaultPathRenderer::GrDefaultPathRenderer() {
 }
@@ -522,8 +520,8 @@
                                                                                viewMatrix;
             renderTargetContext->addDrawOp(
                     clip,
-                    GrNonAAFillRectOp::Make(std::move(paint), viewM, bounds, nullptr, &localMatrix,
-                                            aaType, passes[p]));
+                    GrRectOpFactory::MakeNonAAFillWithLocalMatrix(
+                            std::move(paint), viewM, localMatrix, bounds, aaType, passes[p]));
         } else {
             std::unique_ptr<GrLegacyMeshDrawOp> op =
                     DefaultPathOp::Make(paint.getColor(), path, srcSpaceTol, newCoverage,
diff --git a/src/gpu/ops/GrMSAAPathRenderer.cpp b/src/gpu/ops/GrMSAAPathRenderer.cpp
index ef64ecf..f49c8cd 100644
--- a/src/gpu/ops/GrMSAAPathRenderer.cpp
+++ b/src/gpu/ops/GrMSAAPathRenderer.cpp
@@ -6,7 +6,6 @@
  */
 
 #include "GrMSAAPathRenderer.h"
-
 #include "GrAuditTrail.h"
 #include "GrClip.h"
 #include "GrDefaultGeoProcFactory.h"
@@ -26,7 +25,7 @@
 #include "glsl/GrGLSLUtil.h"
 #include "glsl/GrGLSLVertexShaderBuilder.h"
 #include "ops/GrMeshDrawOp.h"
-#include "ops/GrNonAAFillRectOp.h"
+#include "ops/GrRectOpFactory.h"
 
 static const float kTolerance = 0.5f;
 
@@ -667,8 +666,8 @@
                 (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() : viewMatrix;
         renderTargetContext->addDrawOp(
                 clip,
-                GrNonAAFillRectOp::Make(std::move(paint), viewM, bounds, nullptr, &localMatrix,
-                                        aaType, passes[1]));
+                GrRectOpFactory::MakeNonAAFillWithLocalMatrix(std::move(paint), viewM, localMatrix,
+                                                              bounds, aaType, passes[1]));
     }
     return true;
 }
diff --git a/src/gpu/ops/GrNonAAFillRectOp.cpp b/src/gpu/ops/GrNonAAFillRectOp.cpp
index defc790..0c55959 100644
--- a/src/gpu/ops/GrNonAAFillRectOp.cpp
+++ b/src/gpu/ops/GrNonAAFillRectOp.cpp
@@ -5,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-#include "GrNonAAFillRectOp.h"
 #include "GrAppliedClip.h"
 #include "GrColor.h"
 #include "GrDefaultGeoProcFactory.h"
@@ -14,6 +13,7 @@
 #include "GrOpFlushState.h"
 #include "GrPrimitiveProcessor.h"
 #include "GrQuad.h"
+#include "GrRectOpFactory.h"
 #include "GrResourceProvider.h"
 #include "GrSimpleMeshDrawOpHelper.h"
 #include "SkMatrixPriv.h"
@@ -358,25 +358,45 @@
 
 }  // anonymous namespace
 
-namespace GrNonAAFillRectOp {
+namespace GrRectOpFactory {
 
-std::unique_ptr<GrDrawOp> Make(GrPaint&& paint,
-                               const SkMatrix& viewMatrix,
-                               const SkRect& rect,
-                               const SkRect* localRect,
-                               const SkMatrix* localMatrix,
-                               GrAAType aaType,
-                               const GrUserStencilSettings* stencilSettings) {
-    if (!viewMatrix.hasPerspective() && (!localMatrix || !localMatrix->hasPerspective())) {
-        return NonAAFillRectOp::Make(std::move(paint), viewMatrix, rect, localRect, localMatrix,
-                                     aaType, stencilSettings);
+std::unique_ptr<GrDrawOp> MakeNonAAFill(GrPaint&& paint, const SkMatrix& viewMatrix,
+                                        const SkRect& rect, GrAAType aaType,
+                                        const GrUserStencilSettings* stencilSettings) {
+    if (viewMatrix.hasPerspective()) {
+        return NonAAFillRectPerspectiveOp::Make(std::move(paint), viewMatrix, rect, nullptr,
+                                                nullptr, aaType, stencilSettings);
     } else {
-        return NonAAFillRectPerspectiveOp::Make(std::move(paint), viewMatrix, rect, localRect,
-                                                localMatrix, aaType, stencilSettings);
+        return NonAAFillRectOp::Make(std::move(paint), viewMatrix, rect, nullptr, nullptr, aaType,
+                                     stencilSettings);
     }
 }
 
-};  // namespace GrNonAAFillRectOp
+std::unique_ptr<GrDrawOp> MakeNonAAFillWithLocalMatrix(
+        GrPaint&& paint, const SkMatrix& viewMatrix, const SkMatrix& localMatrix,
+        const SkRect& rect, GrAAType aaType, const GrUserStencilSettings* stencilSettings) {
+    if (viewMatrix.hasPerspective() || localMatrix.hasPerspective()) {
+        return NonAAFillRectPerspectiveOp::Make(std::move(paint), viewMatrix, rect, nullptr,
+                                                &localMatrix, aaType, stencilSettings);
+    } else {
+        return NonAAFillRectOp::Make(std::move(paint), viewMatrix, rect, nullptr, &localMatrix,
+                                     aaType, stencilSettings);
+    }
+}
+
+std::unique_ptr<GrDrawOp> MakeNonAAFillWithLocalRect(GrPaint&& paint, const SkMatrix& viewMatrix,
+                                                     const SkRect& rect, const SkRect& localRect,
+                                                     GrAAType aaType) {
+    if (viewMatrix.hasPerspective()) {
+        return NonAAFillRectPerspectiveOp::Make(std::move(paint), viewMatrix, rect, &localRect,
+                                                nullptr, aaType, nullptr);
+    } else {
+        return NonAAFillRectOp::Make(std::move(paint), viewMatrix, rect, &localRect, nullptr,
+                                     aaType, nullptr);
+    }
+}
+
+}  // namespace GrRectOpFactory
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -385,14 +405,17 @@
     SkRect localRect = GrTest::TestRect(random);
     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
     SkMatrix localMatrix = GrTest::TestMatrix(random);
-    bool hasLocalRect = random->nextBool();
-    bool hasLocalMatrix = random->nextBool();
     const GrUserStencilSettings* stencil = GrGetRandomStencil(random, context);
     GrAAType aaType = GrAAType::kNone;
     if (fsaaType == GrFSAAType::kUnifiedMSAA) {
         aaType = random->nextBool() ? GrAAType::kMSAA : GrAAType::kNone;
     }
-    return GrNonAAFillRectOp::Make(std::move(paint), viewMatrix, rect,
-                                   hasLocalRect ? &localRect : nullptr,
-                                   hasLocalMatrix ? &localMatrix : nullptr, aaType, stencil);
+    const SkRect* lr = random->nextBool() ? &localRect : nullptr;
+    const SkMatrix* lm = random->nextBool() ? &localMatrix : nullptr;
+    if (viewMatrix.hasPerspective() || (lm && lm->hasPerspective())) {
+        return NonAAFillRectOp::Make(std::move(paint), viewMatrix, rect, lr, lm, aaType, stencil);
+    } else {
+        return NonAAFillRectPerspectiveOp::Make(std::move(paint), viewMatrix, rect, lr, lm, aaType,
+                                                stencil);
+    }
 }
diff --git a/src/gpu/ops/GrNonAAFillRectOp.h b/src/gpu/ops/GrNonAAFillRectOp.h
deleted file mode 100644
index 70dd8fd..0000000
--- a/src/gpu/ops/GrNonAAFillRectOp.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrNonAAFillRectOp_DEFINED
-#define GrNonAAFillRectOp_DEFINED
-
-#include <memory>
-#include "GrColor.h"
-#include "GrDrawOp.h"
-
-class GrPaint;
-class SkMatrix;
-struct SkRect;
-struct GrUserStencilSettings;
-enum class GrAAType : unsigned;
-
-namespace GrNonAAFillRectOp {
-std::unique_ptr<GrDrawOp> Make(GrPaint&&,
-                               const SkMatrix& viewMatrix,
-                               const SkRect& rect,
-                               const SkRect* localRect,
-                               const SkMatrix* localMatrix,
-                               GrAAType,
-                               const GrUserStencilSettings* = nullptr);
-};
-
-#endif
diff --git a/src/gpu/ops/GrNonAAStrokeRectOp.cpp b/src/gpu/ops/GrNonAAStrokeRectOp.cpp
index 94dc373..4177b65 100644
--- a/src/gpu/ops/GrNonAAStrokeRectOp.cpp
+++ b/src/gpu/ops/GrNonAAStrokeRectOp.cpp
@@ -5,15 +5,15 @@
  * found in the LICENSE file.
  */
 
-#include "GrNonAAStrokeRectOp.h"
-
 #include "GrColor.h"
 #include "GrDefaultGeoProcFactory.h"
 #include "GrDrawOpTest.h"
 #include "GrMeshDrawOp.h"
 #include "GrOpFlushState.h"
-#include "SkStrokeRec.h"
+#include "GrRectOpFactory.h"
+#include "GrSimpleMeshDrawOpHelper.h"
 #include "SkRandom.h"
+#include "SkStrokeRec.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
@@ -46,7 +46,12 @@
            (stroke.getJoin() == SkPaint::kMiter_Join && stroke.getMiter() > SK_ScalarSqrt2);
 }
 
-class NonAAStrokeRectOp final : public GrLegacyMeshDrawOp {
+namespace {
+
+class NonAAStrokeRectOp final : public GrMeshDrawOp {
+private:
+    using Helper = GrSimpleMeshDrawOpHelper;
+
 public:
     DEFINE_OP_CLASS_ID
 
@@ -58,31 +63,44 @@
                 "Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
                 "StrokeWidth: %.2f\n",
                 fColor, fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom, fStrokeWidth);
-        string.append(DumpPipelineInfo(*this->pipeline()));
         string.append(INHERITED::dumpInfo());
         return string;
     }
 
-    static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
-                                                    const SkRect& rect, const SkStrokeRec& stroke,
-                                                    bool snapToPixelCenters) {
+    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
+                                          const SkRect& rect, const SkStrokeRec& stroke,
+                                          GrAAType aaType) {
         if (!allowed_stroke(stroke)) {
             return nullptr;
         }
-        NonAAStrokeRectOp* op = new NonAAStrokeRectOp();
-        op->fColor = color;
-        op->fViewMatrix = viewMatrix;
-        op->fRect = rect;
-        // Sort the rect for hairlines
-        op->fRect.sort();
-        op->fStrokeWidth = stroke.getWidth();
+        Helper::Flags flags = Helper::Flags::kNone;
+        // Depending on sub-pixel coordinates and the particular GPU, we may lose a corner of
+        // hairline rects. We jam all the vertices to pixel centers to avoid this, but not
+        // when MSAA is enabled because it can cause ugly artifacts.
+        if (stroke.getStyle() == SkStrokeRec::kHairline_Style && aaType != GrAAType::kMSAA) {
+            flags |= Helper::Flags::kSnapVerticesToPixelCenters;
+        }
+        return Helper::FactoryHelper<NonAAStrokeRectOp>(std::move(paint), flags, viewMatrix, rect,
+                                                        stroke, aaType);
+    }
 
-        SkScalar rad = SkScalarHalf(op->fStrokeWidth);
+    NonAAStrokeRectOp(const Helper::MakeArgs& helperArgs, GrColor color, Helper::Flags flags,
+                      const SkMatrix& viewMatrix, const SkRect& rect, const SkStrokeRec& stroke,
+                      GrAAType aaType)
+            : INHERITED(ClassID()), fHelper(helperArgs, aaType, flags) {
+        fColor = color;
+        fViewMatrix = viewMatrix;
+        fRect = rect;
+        // Sort the rect for hairlines
+        fRect.sort();
+        fStrokeWidth = stroke.getWidth();
+
+        SkScalar rad = SkScalarHalf(fStrokeWidth);
         SkRect bounds = rect;
         bounds.outset(rad, rad);
 
         // If our caller snaps to pixel centers then we have to round out the bounds
-        if (snapToPixelCenters) {
+        if (flags & Helper::Flags::kSnapVerticesToPixelCenters) {
             viewMatrix.mapRect(&bounds);
             // We want to be consistent with how we snap non-aa lines. To match what we do in
             // GrGLSLVertexShaderBuilder, we first floor all the vertex values and then add half a
@@ -92,28 +110,27 @@
                        SkScalarFloorToScalar(bounds.fRight),
                        SkScalarFloorToScalar(bounds.fBottom));
             bounds.offset(0.5f, 0.5f);
-            op->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
+            this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
         } else {
-            op->setTransformedBounds(bounds, op->fViewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
+            this->setTransformedBounds(bounds, fViewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
         }
-        return std::unique_ptr<GrLegacyMeshDrawOp>(op);
+    }
+
+    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
+
+    bool xpRequiresDstTexture(const GrCaps& caps, const GrAppliedClip* clip) override {
+        bool result = fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kNone,
+                                                   &fColor);
+        return result;
     }
 
 private:
-    NonAAStrokeRectOp() : INHERITED(ClassID()) {}
-
-    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
-                                    GrProcessorAnalysisCoverage* coverage) const override {
-        color->setToConstant(fColor);
-        *coverage = GrProcessorAnalysisCoverage::kNone;
-    }
-
     void onPrepareDraws(Target* target) const override {
         sk_sp<GrGeometryProcessor> gp;
         {
             using namespace GrDefaultGeoProcFactory;
             Color color(fColor);
-            LocalCoords::Type localCoordsType = fNeedsLocalCoords
+            LocalCoords::Type localCoordsType = fHelper.usesLocalCoords()
                                                         ? LocalCoords::kUsePosition_Type
                                                         : LocalCoords::kUnused_Type;
             gp = GrDefaultGeoProcFactory::Make(color, Coverage::kSolid_Type, localCoordsType,
@@ -159,12 +176,7 @@
         GrMesh mesh(primType);
         mesh.setNonIndexedNonInstanced(vertexCount);
         mesh.setVertexData(vertexBuffer, firstVertex);
-        target->draw(gp.get(), this->pipeline(), mesh);
-    }
-
-    void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
-        optimizations.getOverrideColorIfSet(&fColor);
-        fNeedsLocalCoords = optimizations.readsLocalCoords();
+        target->draw(gp.get(), fHelper.makePipeline(target), mesh);
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps&) override {
@@ -173,42 +185,46 @@
         return false;
     }
 
+    Helper fHelper;
     GrColor fColor;
     SkMatrix fViewMatrix;
     SkRect fRect;
     SkScalar fStrokeWidth;
-    bool fNeedsLocalCoords;
 
     const static int kVertsPerHairlineRect = 5;
     const static int kVertsPerStrokeRect = 10;
 
-    typedef GrLegacyMeshDrawOp INHERITED;
+    typedef GrMeshDrawOp INHERITED;
 };
 
-namespace GrNonAAStrokeRectOp {
+}  // anonymous namespace
 
-std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
-                                         const SkMatrix& viewMatrix,
-                                         const SkRect& rect,
-                                         const SkStrokeRec& stroke,
-                                         bool snapToPixelCenters) {
-    return NonAAStrokeRectOp::Make(color, viewMatrix, rect, stroke, snapToPixelCenters);
+namespace GrRectOpFactory {
+std::unique_ptr<GrDrawOp> MakeNonAAStroke(GrPaint&& paint,
+                                          const SkMatrix& viewMatrix,
+                                          const SkRect& rect,
+                                          const SkStrokeRec& stroke,
+                                          GrAAType aaType) {
+    return NonAAStrokeRectOp::Make(std::move(paint), viewMatrix, rect, stroke, aaType);
 }
-}
+}  // namespace GrRectOpFactory
 
 #if GR_TEST_UTILS
 
-GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(NonAAStrokeRectOp) {
+GR_DRAW_OP_TEST_DEFINE(NonAAStrokeRectOp) {
     SkMatrix viewMatrix = GrTest::TestMatrix(random);
-    GrColor color = GrRandomColor(random);
     SkRect rect = GrTest::TestRect(random);
     SkScalar strokeWidth = random->nextBool() ? 0.0f : 2.0f;
-    SkPaint paint;
-    paint.setStrokeWidth(strokeWidth);
-    paint.setStyle(SkPaint::kStroke_Style);
-    paint.setStrokeJoin(SkPaint::kMiter_Join);
-    SkStrokeRec strokeRec(paint);
-    return GrNonAAStrokeRectOp::Make(color, viewMatrix, rect, strokeRec, random->nextBool());
+    SkPaint strokePaint;
+    strokePaint.setStrokeWidth(strokeWidth);
+    strokePaint.setStyle(SkPaint::kStroke_Style);
+    strokePaint.setStrokeJoin(SkPaint::kMiter_Join);
+    SkStrokeRec strokeRec(strokePaint);
+    GrAAType aaType = GrAAType::kNone;
+    if (fsaaType == GrFSAAType::kUnifiedMSAA) {
+        aaType = random->nextBool() ? GrAAType::kMSAA : GrAAType::kNone;
+    }
+    return NonAAStrokeRectOp::Make(std::move(paint), viewMatrix, rect, strokeRec, aaType);
 }
 
 #endif
diff --git a/src/gpu/ops/GrNonAAStrokeRectOp.h b/src/gpu/ops/GrNonAAStrokeRectOp.h
deleted file mode 100644
index 4071cfd..0000000
--- a/src/gpu/ops/GrNonAAStrokeRectOp.h
+++ /dev/null
@@ -1,28 +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 GrNonAAStrokeRectOp_DEFINED
-#define GrNonAAStrokeRectOp_DEFINED
-
-#include "GrColor.h"
-#include "SkRefCnt.h"
-
-class GrLegacyMeshDrawOp;
-struct SkRect;
-class SkStrokeRec;
-class SkMatrix;
-
-namespace GrNonAAStrokeRectOp {
-
-std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
-                                         const SkMatrix& viewMatrix,
-                                         const SkRect& rect,
-                                         const SkStrokeRec&,
-                                         bool snapToPixelCenters);
-}
-
-#endif
diff --git a/src/gpu/ops/GrRectOpFactory.cpp b/src/gpu/ops/GrRectOpFactory.cpp
deleted file mode 100644
index 45e2053..0000000
--- a/src/gpu/ops/GrRectOpFactory.cpp
+++ /dev/null
@@ -1,34 +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 "GrRectOpFactory.h"
-
-#include "GrAAStrokeRectOp.h"
-#include "GrMeshDrawOp.h"
-#include "SkStrokeRec.h"
-
-namespace GrRectOpFactory {
-
-std::unique_ptr<GrLegacyMeshDrawOp> MakeAAFillNestedRects(GrColor color,
-                                                          const SkMatrix& viewMatrix,
-                                                          const SkRect rects[2]) {
-    SkASSERT(viewMatrix.rectStaysRect());
-    SkASSERT(!rects[0].isEmpty() && !rects[1].isEmpty());
-
-    SkRect devOutside, devInside;
-    viewMatrix.mapRect(&devOutside, rects[0]);
-    viewMatrix.mapRect(&devInside, rects[1]);
-    if (devInside.isEmpty()) {
-        if (devOutside.isEmpty()) {
-            return nullptr;
-        }
-        return GrAAFillRectOp::Make(color, viewMatrix, devOutside, devOutside);
-    }
-
-    return GrAAStrokeRectOp::MakeFillBetweenRects(color, viewMatrix, devOutside, devInside);
-}
-};
diff --git a/src/gpu/ops/GrRectOpFactory.h b/src/gpu/ops/GrRectOpFactory.h
index 58927ef..cf3da2f 100644
--- a/src/gpu/ops/GrRectOpFactory.h
+++ b/src/gpu/ops/GrRectOpFactory.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015 Google Inc.
+ * Copyright 2017 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
@@ -8,58 +8,66 @@
 #ifndef GrRectOpFactory_DEFINED
 #define GrRectOpFactory_DEFINED
 
-#include "GrAAFillRectOp.h"
-#include "GrAAStrokeRectOp.h"
-#include "GrAnalyticRectOp.h"
-#include "GrColor.h"
-#include "GrMeshDrawOp.h"
-#include "GrNonAAStrokeRectOp.h"
-#include "GrPaint.h"
-#include "SkMatrix.h"
-#include "SkRefCnt.h"
+#include <memory>
+#include "GrTypes.h"
 
+enum class GrAAType : unsigned;
+class GrDrawOp;
+class GrPaint;
+struct GrUserStencilSettings;
+class SkMatrix;
 struct SkRect;
 class SkStrokeRec;
 
 /**
- * A factory for returning GrDrawOps which can draw rectangles.
+ * A set of factory functions for drawing rectangles including fills, strokes, coverage-antialiased,
+ * and non-antialiased. The non-antialiased ops can be used with MSAA. As with other GrDrawOp
+ * factories, the GrPaint is only consumed by these methods if a valid op is returned. If null is
+ * returned then the paint is unmodified and may still be used.
  */
 namespace GrRectOpFactory {
+/** AA Fill */
 
-inline std::unique_ptr<GrLegacyMeshDrawOp> MakeAAFill(const GrPaint& paint,
-                                                      const SkMatrix& viewMatrix,
-                                                      const SkRect& rect,
-                                                      const SkRect& croppedRect,
-                                                      const SkRect& devRect) {
-    return GrAAFillRectOp::Make(paint.getColor(), viewMatrix, croppedRect, devRect);
-}
+std::unique_ptr<GrDrawOp> MakeAAFill(GrPaint&&, const SkMatrix&, const SkRect&,
+                                     const GrUserStencilSettings* = nullptr);
 
-inline std::unique_ptr<GrLegacyMeshDrawOp> MakeAAFill(GrColor color,
-                                                      const SkMatrix& viewMatrix,
-                                                      const SkMatrix& localMatrix,
-                                                      const SkRect& rect,
-                                                      const SkRect& devRect) {
-    return GrAAFillRectOp::Make(color, viewMatrix, localMatrix, rect, devRect);
-}
+// Using this method to supply a device rect simply saves recalculation if the device rect is known.
+std::unique_ptr<GrDrawOp> MakeAAFillWithDevRect(GrPaint&&, const SkMatrix& viewMatrix,
+                                                const SkRect& rect, const SkRect& devRect);
 
-inline std::unique_ptr<GrLegacyMeshDrawOp> MakeNonAAStroke(GrColor color,
-                                                           const SkMatrix& viewMatrix,
-                                                           const SkRect& rect,
-                                                           const SkStrokeRec& strokeRec,
-                                                           bool snapToPixelCenters) {
-    return GrNonAAStrokeRectOp::Make(color, viewMatrix, rect, strokeRec, snapToPixelCenters);
-}
+std::unique_ptr<GrDrawOp> MakeAAFillWithLocalMatrix(GrPaint&&, const SkMatrix& viewMatrix,
+                                                    const SkMatrix& localMatrix, const SkRect&);
 
-inline std::unique_ptr<GrLegacyMeshDrawOp> MakeAAStroke(GrColor color,
-                                                        const SkMatrix& viewMatrix,
-                                                        const SkRect& rect,
-                                                        const SkStrokeRec& stroke) {
-    return GrAAStrokeRectOp::Make(color, viewMatrix, rect, stroke);
-}
+std::unique_ptr<GrDrawOp> MakeAAFillWithLocalRect(GrPaint&&, const SkMatrix&, const SkRect& rect,
+                                                  const SkRect& localRect);
 
-// First rect is outer; second rect is inner
-std::unique_ptr<GrLegacyMeshDrawOp> MakeAAFillNestedRects(GrColor, const SkMatrix& viewMatrix,
-                                                          const SkRect rects[2]);
-};
+/** Non-AA Fill - GrAAType must be either kNone or kMSAA. */
+
+std::unique_ptr<GrDrawOp> MakeNonAAFill(GrPaint&&, const SkMatrix& viewMatrix, const SkRect& rect,
+                                        GrAAType, const GrUserStencilSettings* = nullptr);
+
+std::unique_ptr<GrDrawOp> MakeNonAAFillWithLocalMatrix(GrPaint&&, const SkMatrix& viewMatrix,
+                                                       const SkMatrix& localMatrix, const SkRect&,
+                                                       GrAAType,
+                                                       const GrUserStencilSettings* = nullptr);
+
+std::unique_ptr<GrDrawOp> MakeNonAAFillWithLocalRect(GrPaint&&, const SkMatrix&, const SkRect& rect,
+                                                     const SkRect& localRect, GrAAType);
+
+/** AA Stroke */
+
+std::unique_ptr<GrDrawOp> MakeAAStroke(GrPaint&&, const SkMatrix&, const SkRect&,
+                                       const SkStrokeRec&);
+
+// rects[0] == outer rectangle, rects[1] == inner rectangle. Null return means there is nothing to
+// draw rather than failure.
+std::unique_ptr<GrDrawOp> MakeAAFillNestedRects(GrPaint&&, const SkMatrix&, const SkRect rects[2]);
+
+/** Non-AA Stroke - GrAAType must be either kNone or kMSAA. */
+
+std::unique_ptr<GrDrawOp> MakeNonAAStroke(GrPaint&&, const SkMatrix&, const SkRect&,
+                                          const SkStrokeRec&, GrAAType);
+
+}  // namespace GrRectOpFactory
 
 #endif
diff --git a/src/gpu/ops/GrSimpleMeshDrawOpHelper.h b/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
index dcd8dd8..903859c 100644
--- a/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
+++ b/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
@@ -35,19 +35,26 @@
     template <typename Op, typename... OpArgs>
     static std::unique_ptr<GrDrawOp> FactoryHelper(GrPaint&& paint, OpArgs... opArgs);
 
-    GrSimpleMeshDrawOpHelper(const MakeArgs& args, GrAAType aaType,
-                             GrUserStencilSettings* stencilSettings = nullptr)
+    enum class Flags : uint32_t {
+        kNone = 0x0,
+        kSnapVerticesToPixelCenters = 0x1,
+    };
+    GR_DECL_BITFIELD_CLASS_OPS_FRIENDS(Flags);
+
+    GrSimpleMeshDrawOpHelper(const MakeArgs& args, GrAAType aaType, Flags flags = Flags::kNone)
             : fProcessors(args.fProcessorSet)
             , fPipelineFlags(args.fSRGBFlags)
             , fAAType((int)aaType)
             , fRequiresDstTexture(false)
             , fUsesLocalCoords(false)
             , fCompatibleWithAlphaAsCoveage(false) {
-        SkASSERT(!stencilSettings);
         SkDEBUGCODE(fDidAnalysis = false);
         if (GrAATypeIsHW(aaType)) {
             fPipelineFlags |= GrPipeline::kHWAntialias_Flag;
         }
+        if (flags & Flags::kSnapVerticesToPixelCenters) {
+            fPipelineFlags |= GrPipeline::kSnapVerticesToPixelCenters_Flag;
+        }
     }
 
     ~GrSimpleMeshDrawOpHelper() {
@@ -167,6 +174,7 @@
 class GrSimpleMeshDrawOpHelperWithStencil : private GrSimpleMeshDrawOpHelper {
 public:
     using MakeArgs = GrSimpleMeshDrawOpHelper::MakeArgs;
+    using Flags = GrSimpleMeshDrawOpHelper::Flags;
 
     // using declarations can't be templated, so this is a pass through function instead.
     template <typename Op, typename... OpArgs>
@@ -176,8 +184,9 @@
     }
 
     GrSimpleMeshDrawOpHelperWithStencil(const MakeArgs& args, GrAAType aaType,
-                                         const GrUserStencilSettings* stencilSettings)
-            : INHERITED(args, aaType)
+                                        const GrUserStencilSettings* stencilSettings,
+                                        Flags flags = Flags::kNone)
+            : INHERITED(args, aaType, flags)
             , fStencilSettings(stencilSettings ? stencilSettings
                                                : &GrUserStencilSettings::kUnused) {}
 
@@ -191,6 +200,7 @@
 
     using GrSimpleMeshDrawOpHelper::xpRequiresDstTexture;
     using GrSimpleMeshDrawOpHelper::usesLocalCoords;
+    using GrSimpleMeshDrawOpHelper::compatibleWithAlphaAsCoverage;
 
     bool isCompatible(const GrSimpleMeshDrawOpHelperWithStencil& that, const GrCaps& caps,
                       const SkRect& aBounds, const SkRect& bBounds) const {
@@ -227,4 +237,6 @@
     }
 }
 
+GR_MAKE_BITFIELD_CLASS_OPS(GrSimpleMeshDrawOpHelper::Flags)
+
 #endif
diff --git a/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp b/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp
index 46e9fa7..a613841 100644
--- a/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp
+++ b/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp
@@ -17,7 +17,7 @@
 #include "GrResourceProvider.h"
 #include "GrStencilPathOp.h"
 #include "GrStyle.h"
-#include "ops/GrNonAAFillRectOp.h"
+#include "ops/GrRectOpFactory.h"
 
 GrPathRenderer* GrStencilAndCoverPathRenderer::Create(GrResourceProvider* resourceProvider,
                                                       const GrCaps& caps) {
@@ -134,10 +134,10 @@
             if (GrAAType::kMixedSamples == coverAAType) {
                 coverAAType = GrAAType::kNone;
             }
-            args.fRenderTargetContext->addDrawOp(
-                    *args.fClip,
-                    GrNonAAFillRectOp::Make(std::move(args.fPaint), viewM, bounds, nullptr, &invert,
-                                            coverAAType, &kInvertedCoverPass));
+            args.fRenderTargetContext->addDrawOp(*args.fClip,
+                                                 GrRectOpFactory::MakeNonAAFillWithLocalMatrix(
+                                                         std::move(args.fPaint), viewM, invert,
+                                                         bounds, coverAAType, &kInvertedCoverPass));
         }
     } else {
         std::unique_ptr<GrDrawOp> op =
diff --git a/tests/ProcessorTest.cpp b/tests/ProcessorTest.cpp
index 528dd61..7f9dff7 100644
--- a/tests/ProcessorTest.cpp
+++ b/tests/ProcessorTest.cpp
@@ -19,7 +19,7 @@
 #include "GrResourceProvider.h"
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "ops/GrNonAAFillRectOp.h"
+#include "ops/GrRectOpFactory.h"
 #include "ops/GrTestMeshDrawOp.h"
 
 namespace {
@@ -270,9 +270,9 @@
     paint.addColorFragmentProcessor(std::move(fp));
     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
 
-    auto op = GrNonAAFillRectOp::Make(std::move(paint), SkMatrix::I(),
-                                      SkRect::MakeWH(rtc->width(), rtc->height()), nullptr, nullptr,
-                                      GrAAType::kNone);
+    auto op = GrRectOpFactory::MakeNonAAFill(std::move(paint), SkMatrix::I(),
+                                             SkRect::MakeWH(rtc->width(), rtc->height()),
+                                             GrAAType::kNone);
     rtc->addDrawOp(GrNoClip(), std::move(op));
 }
 
diff --git a/tools/gpu/GrTest.cpp b/tools/gpu/GrTest.cpp
index 0486787..1eb9860 100644
--- a/tools/gpu/GrTest.cpp
+++ b/tools/gpu/GrTest.cpp
@@ -472,25 +472,24 @@
 #define DRAW_OP_TEST_ENTRY(Op) Op##__Test
 
 LEGACY_MESH_DRAW_OP_TEST_EXTERN(AAConvexPathOp);
-LEGACY_MESH_DRAW_OP_TEST_EXTERN(AAFillRectOp);
-LEGACY_MESH_DRAW_OP_TEST_EXTERN(AAFillRectOpLocalMatrix);
 LEGACY_MESH_DRAW_OP_TEST_EXTERN(AAFlatteningConvexPathOp)
 LEGACY_MESH_DRAW_OP_TEST_EXTERN(AAHairlineOp);
-LEGACY_MESH_DRAW_OP_TEST_EXTERN(AAStrokeRectOp);
 LEGACY_MESH_DRAW_OP_TEST_EXTERN(AnalyticRectOp);
 LEGACY_MESH_DRAW_OP_TEST_EXTERN(DashOp);
 LEGACY_MESH_DRAW_OP_TEST_EXTERN(DefaultPathOp);
 LEGACY_MESH_DRAW_OP_TEST_EXTERN(GrDrawAtlasOp);
-LEGACY_MESH_DRAW_OP_TEST_EXTERN(NonAAStrokeRectOp);
 LEGACY_MESH_DRAW_OP_TEST_EXTERN(SmallPathOp);
 LEGACY_MESH_DRAW_OP_TEST_EXTERN(TesselatingPathOp);
 LEGACY_MESH_DRAW_OP_TEST_EXTERN(TextBlobOp);
 LEGACY_MESH_DRAW_OP_TEST_EXTERN(VerticesOp);
 
+DRAW_OP_TEST_EXTERN(AAFillRectOp)
+DRAW_OP_TEST_EXTERN(AAStrokeRectOp);
 DRAW_OP_TEST_EXTERN(CircleOp)
 DRAW_OP_TEST_EXTERN(DIEllipseOp);
 DRAW_OP_TEST_EXTERN(EllipseOp);
 DRAW_OP_TEST_EXTERN(NonAAFillRectOp)
+DRAW_OP_TEST_EXTERN(NonAAStrokeRectOp);
 DRAW_OP_TEST_EXTERN(RRectOp);
 
 void GrDrawRandomOp(SkRandom* random, GrRenderTargetContext* renderTargetContext, GrPaint&& paint) {
@@ -498,16 +497,12 @@
     using MakeTestLegacyMeshDrawOpFn = std::unique_ptr<GrLegacyMeshDrawOp>(SkRandom*, GrContext*);
     static constexpr MakeTestLegacyMeshDrawOpFn* gLegacyFactories[] = {
         DRAW_OP_TEST_ENTRY(AAConvexPathOp),
-        DRAW_OP_TEST_ENTRY(AAFillRectOp),
-        DRAW_OP_TEST_ENTRY(AAFillRectOpLocalMatrix),
         DRAW_OP_TEST_ENTRY(AAFlatteningConvexPathOp),
         DRAW_OP_TEST_ENTRY(AAHairlineOp),
-        DRAW_OP_TEST_ENTRY(AAStrokeRectOp),
         DRAW_OP_TEST_ENTRY(AnalyticRectOp),
         DRAW_OP_TEST_ENTRY(DashOp),
         DRAW_OP_TEST_ENTRY(DefaultPathOp),
         DRAW_OP_TEST_ENTRY(GrDrawAtlasOp),
-        DRAW_OP_TEST_ENTRY(NonAAStrokeRectOp),
         DRAW_OP_TEST_ENTRY(SmallPathOp),
         DRAW_OP_TEST_ENTRY(TesselatingPathOp),
         DRAW_OP_TEST_ENTRY(TextBlobOp),
@@ -516,10 +511,13 @@
 
     using MakeDrawOpFn = std::unique_ptr<GrDrawOp>(GrPaint&&, SkRandom*, GrContext*, GrFSAAType);
     static constexpr MakeDrawOpFn* gFactories[] = {
+        DRAW_OP_TEST_ENTRY(AAFillRectOp),
+        DRAW_OP_TEST_ENTRY(AAStrokeRectOp),
         DRAW_OP_TEST_ENTRY(CircleOp),
         DRAW_OP_TEST_ENTRY(DIEllipseOp),
         DRAW_OP_TEST_ENTRY(EllipseOp),
         DRAW_OP_TEST_ENTRY(NonAAFillRectOp),
+        DRAW_OP_TEST_ENTRY(NonAAStrokeRectOp),
         DRAW_OP_TEST_ENTRY(RRectOp),
     };