Convert NonAALatticeOp to non-legacy GrMeshDrawOp.

Also adds a test factory and fixes a bug in the lattice iter validator where it compared each div value against the start rather than the previous div.

Bug: skia:
Change-Id: I30e9ddfcbaab7829a2f646ad851f99d1e518ab4a
Reviewed-on: https://skia-review.googlesource.com/21871
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/core/SkLatticeIter.cpp b/src/core/SkLatticeIter.cpp
index 4fe9352..9b2a279 100644
--- a/src/core/SkLatticeIter.cpp
+++ b/src/core/SkLatticeIter.cpp
@@ -17,6 +17,7 @@
         if (prev >= divs[i] || divs[i] >= end) {
             return false;
         }
+        prev = divs[i];
     }
 
     return true;
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index e9be8d6..2a13ea3 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -1415,11 +1415,9 @@
 
     AutoCheckFlush acf(this->drawingManager());
 
-    std::unique_ptr<GrLegacyMeshDrawOp> op = GrLatticeOp::MakeNonAA(
-            paint.getColor(), viewMatrix, imageWidth, imageHeight, std::move(iter), dst);
-
-    GrPipelineBuilder pipelineBuilder(std::move(paint), GrAAType::kNone);
-    this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
+    std::unique_ptr<GrDrawOp> op = GrLatticeOp::MakeNonAA(std::move(paint), viewMatrix, imageWidth,
+                                                          imageHeight, std::move(iter), dst);
+    this->addDrawOp(clip, std::move(op));
 }
 
 bool GrRenderTargetContext::prepareForExternalIO(int numSemaphores,
diff --git a/src/gpu/ops/GrLatticeOp.cpp b/src/gpu/ops/GrLatticeOp.cpp
index 50b957c..3e35cb4 100644
--- a/src/gpu/ops/GrLatticeOp.cpp
+++ b/src/gpu/ops/GrLatticeOp.cpp
@@ -6,11 +6,12 @@
  */
 
 #include "GrLatticeOp.h"
-
 #include "GrDefaultGeoProcFactory.h"
+#include "GrDrawOpTest.h"
 #include "GrMeshDrawOp.h"
 #include "GrOpFlushState.h"
 #include "GrResourceProvider.h"
+#include "GrSimpleMeshDrawOpHelper.h"
 #include "SkBitmap.h"
 #include "SkLatticeIter.h"
 #include "SkRect.h"
@@ -21,16 +22,29 @@
                                          LocalCoords::kHasExplicit_Type, SkMatrix::I());
 }
 
-class NonAALatticeOp final : public GrLegacyMeshDrawOp {
+namespace {
+
+class NonAALatticeOp final : public GrMeshDrawOp {
+private:
+    using Helper = GrSimpleMeshDrawOpHelper;
+
 public:
     DEFINE_OP_CLASS_ID
 
     static const int kVertsPerRect = 4;
     static const int kIndicesPerRect = 6;
 
-    NonAALatticeOp(GrColor color, const SkMatrix& viewMatrix, int imageWidth, int imageHeight,
-                   std::unique_ptr<SkLatticeIter> iter, const SkRect& dst)
-            : INHERITED(ClassID()) {
+    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
+                                          int imageWidth, int imageHeight,
+                                          std::unique_ptr<SkLatticeIter> iter, const SkRect& dst) {
+        return Helper::FactoryHelper<NonAALatticeOp>(std::move(paint), viewMatrix, imageWidth,
+                                                     imageHeight, std::move(iter), dst);
+    }
+
+    NonAALatticeOp(Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
+                   int imageWidth, int imageHeight, std::unique_ptr<SkLatticeIter> iter,
+                   const SkRect& dst)
+            : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kNone) {
         Patch& patch = fPatches.push_back();
         patch.fViewMatrix = viewMatrix;
         patch.fColor = color;
@@ -55,22 +69,19 @@
                         fPatches[i].fDst.fRight, fPatches[i].fDst.fBottom);
         }
 
-        str.append(DumpPipelineInfo(*this->pipeline()));
-        str.append(INHERITED::dumpInfo());
+        str += fHelper.dumpInfo();
+        str += INHERITED::dumpInfo();
         return str;
     }
 
+    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
+
+    RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
+        return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kNone,
+                                            &fPatches.front().fColor);
+    }
+
 private:
-    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
-                                    GrProcessorAnalysisCoverage* coverage) const override {
-        color->setToUnknown();
-        *coverage = GrProcessorAnalysisCoverage::kNone;
-    }
-
-    void applyPipelineOptimizations(const PipelineOptimizations& analysioptimizations) override {
-        analysioptimizations.getOverrideColorIfSet(&fPatches[0].fColor);
-    }
-
     void onPrepareDraws(Target* target) const override {
         sk_sp<GrGeometryProcessor> gp(create_gp());
         if (!gp) {
@@ -133,13 +144,12 @@
                         positions, vertexStride, kVertsPerRect * patch.fIter->numRectsToDraw());
             }
         }
-        helper.recordDraw(target, gp.get(), this->pipeline());
+        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
         NonAALatticeOp* that = t->cast<NonAALatticeOp>();
-        if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
-                                    that->bounds(), caps)) {
+        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
             return false;
         }
 
@@ -158,19 +168,106 @@
         GrColor fColor;
     };
 
+    Helper fHelper;
+    SkSTArray<1, Patch, true> fPatches;
     int fImageWidth;
     int fImageHeight;
-    SkSTArray<1, Patch, true> fPatches;
 
-    typedef GrLegacyMeshDrawOp INHERITED;
+    typedef GrMeshDrawOp INHERITED;
 };
 
+}  // anonymous namespace
+
 namespace GrLatticeOp {
-std::unique_ptr<GrLegacyMeshDrawOp> MakeNonAA(GrColor color, const SkMatrix& viewMatrix,
-                                              int imageWidth, int imageHeight,
-                                              std::unique_ptr<SkLatticeIter> iter,
-                                              const SkRect& dst) {
-    return std::unique_ptr<GrLegacyMeshDrawOp>(
-            new NonAALatticeOp(color, viewMatrix, imageWidth, imageHeight, std::move(iter), dst));
+std::unique_ptr<GrDrawOp> MakeNonAA(GrPaint&& paint, const SkMatrix& viewMatrix, int imageWidth,
+                                    int imageHeight, std::unique_ptr<SkLatticeIter> iter,
+                                    const SkRect& dst) {
+    return NonAALatticeOp::Make(std::move(paint), viewMatrix, imageWidth, imageHeight,
+                                std::move(iter), dst);
 }
 };
+
+#if GR_TEST_UTILS
+
+/** Randomly divides subset into count divs. */
+static void init_random_divs(int divs[], int count, int subsetStart, int subsetStop,
+                             SkRandom* random) {
+    // Rules for lattice divs: Must be strictly increasing and in the range
+    // [subsetStart, subsetStop).
+    // Not terribly efficient alg for generating random divs:
+    // 1) Start with minimum legal pixels between each div.
+    // 2) Randomly assign the remaining pixels of the subset to divs.
+    // 3) Convert from pixel counts to div offsets.
+
+    // 1) Initially each divs[i] represents the number of pixels between
+    // div i-1 and i. The initial div is allowed to be at subsetStart. There
+    // must be one pixel spacing between subsequent divs.
+    divs[0] = 0;
+    for (int i = 1; i < count; ++i) {
+        divs[i] = 1;
+    }
+    // 2) Assign the remaining subset pixels to fall
+    int subsetLength = subsetStop - subsetStart;
+    for (int i = 0; i < subsetLength - count; ++i) {
+        // +1 because count divs means count+1 intervals.
+        int entry = random->nextULessThan(count + 1);
+        // We don't have an entry to  to store the count after the last div
+        if (entry < count) {
+            divs[entry]++;
+        }
+    }
+    // 3) Now convert the counts between divs to pixel indices, incorporating the subset's offset.
+    int offset = subsetStart;
+    for (int i = 0; i < count; ++i) {
+        divs[i] += offset;
+        offset = divs[i];
+    }
+}
+
+GR_DRAW_OP_TEST_DEFINE(NonAALatticeOp) {
+    int imgW = random->nextRangeU(1, 1000);
+    int imgH = random->nextRangeU(1, 1000);
+    SkCanvas::Lattice lattice;
+    SkIRect subset;
+    if (random->nextBool()) {
+        subset.fLeft = random->nextULessThan(imgW);
+        subset.fRight = random->nextRangeU(subset.fLeft + 1, imgW);
+        subset.fTop = random->nextULessThan(imgH);
+        subset.fBottom = random->nextRangeU(subset.fTop + 1, imgH);
+    } else {
+        subset.setXYWH(0, 0, imgW, imgH);
+    }
+    // SkCanvas::Lattice allows bounds to be null. However, SkCanvas creates a temp Lattice with a
+    // non-null bounds before creating a SkLatticeIter since SkLatticeIter requires a bounds.
+    lattice.fBounds = &subset;
+    lattice.fXCount = random->nextRangeU(1, subset.width());
+    lattice.fYCount = random->nextRangeU(1, subset.height());
+    std::unique_ptr<int[]> xdivs(new int[lattice.fXCount]);
+    std::unique_ptr<int[]> ydivs(new int[lattice.fYCount]);
+    init_random_divs(xdivs.get(), lattice.fXCount, subset.fLeft, subset.fRight, random);
+    init_random_divs(ydivs.get(), lattice.fYCount, subset.fTop, subset.fBottom, random);
+    lattice.fXDivs = xdivs.get();
+    lattice.fYDivs = ydivs.get();
+    bool hasFlags = random->nextBool();
+    std::unique_ptr<SkCanvas::Lattice::Flags[]> flags;
+    if (hasFlags) {
+        int n = (lattice.fXCount + 1) * (lattice.fYCount + 1);
+        flags.reset(new SkCanvas::Lattice::Flags[n]);
+        for (int i = 0; i < n; ++i) {
+            flags[i] = random->nextBool() ? SkCanvas::Lattice::kTransparent_Flags
+                                          : (SkCanvas::Lattice::Flags)0;
+        }
+    }
+    lattice.fFlags = flags.get();
+    SkRect dst;
+    dst.fLeft = random->nextRangeScalar(-2000.5f, 1000.f);
+    dst.fTop = random->nextRangeScalar(-2000.5f, 1000.f);
+    dst.fRight = dst.fLeft + random->nextRangeScalar(0.5f, 1000.f);
+    dst.fBottom = dst.fTop + random->nextRangeScalar(0.5f, 1000.f);
+    std::unique_ptr<SkLatticeIter> iter(new SkLatticeIter(lattice, dst));
+    SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
+    SkASSERT(SkLatticeIter::Valid(imgW, imgH, lattice));
+    return NonAALatticeOp::Make(std::move(paint), viewMatrix, imgW, imgH, std::move(iter), dst);
+}
+
+#endif
diff --git a/src/gpu/ops/GrLatticeOp.h b/src/gpu/ops/GrLatticeOp.h
index 65aa622..cce8f32 100644
--- a/src/gpu/ops/GrLatticeOp.h
+++ b/src/gpu/ops/GrLatticeOp.h
@@ -8,19 +8,19 @@
 #ifndef GLatticeOp_DEFINED
 #define GLatticeOp_DEFINED
 
-#include "GrColor.h"
-#include "SkRefCnt.h"
+#include <memory>
+#include "GrTypes.h"
 
-class GrLegacyMeshDrawOp;
+class GrDrawOp;
+class GrPaint;
 class SkLatticeIter;
 class SkMatrix;
 struct SkRect;
 
 namespace GrLatticeOp {
-std::unique_ptr<GrLegacyMeshDrawOp> MakeNonAA(GrColor color, const SkMatrix& viewMatrix,
-                                              int imageWidth, int imageHeight,
-                                              std::unique_ptr<SkLatticeIter> iter,
-                                              const SkRect& dst);
+std::unique_ptr<GrDrawOp> MakeNonAA(GrPaint&& paint, const SkMatrix& viewMatrix, int imageWidth,
+                                    int imageHeight, std::unique_ptr<SkLatticeIter> iter,
+                                    const SkRect& dst);
 };
 
 #endif