Add refcnt'ed immutable vertices class for SkCanvas::drawVertices.

Change-Id: I44a62f5efc674d0adbbf4a33690c3ded9fab3803
Reviewed-on: https://skia-review.googlesource.com/8040
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index b2bc9ed..d58b7ea 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -42,6 +42,7 @@
 class SkSurface;
 class SkSurface_Base;
 class SkTextBlob;
+class SkVertices;
 
 /** \class SkCanvas
 
@@ -1152,9 +1153,9 @@
                     in _texture_ space (not uv space) for each vertex.
         @param colors May be null. If not null, specifies a color for each
                       vertex, to be interpolated across the triangle.
-        @param mode Used if both texs and colors are present. In this
-                    case the colors are combined with the texture using mode,
-                    before being drawn using the paint. 
+        @param mode Used if both texs and colors are present and paint has a
+                    shader. In this case the colors are combined with the texture
+                    using mode, before being drawn using the paint.
         @param indices If not null, array of indices to reference into the
                     vertex (texs, colors) array.
         @param indexCount number of entries in the indices array (if not null)
@@ -1172,6 +1173,23 @@
         this->drawVertices(vmode, vertexCount, vertices, texs, colors, SkBlendMode::kModulate,
                            indices, indexCount, paint);
     }
+    enum VerticesFlags {
+        /** Ignore the vertices' colors and instead use the paint color. */
+        kIgnoreColors_VerticesFlag = 0x1,
+        /** Ignore the vertices' tex coords (and any shader on the paint). */
+        kIgnoreTexCoords_VerticesFlag = 0x2
+    };
+    /** Draw vertices from an immutable SkVertices object.
+
+        @param vertices The mesh to draw.
+        @param mode Used if both texs and colors are present and paint has a
+                    shader. In this case the colors are combined with the texture
+                    using mode, before being drawn using the paint.
+        @param paint Specifies the shader/texture if present.
+        @param flags Allows the caller to ignore colors or texs on vertices.
+     */
+    void drawVertices(sk_sp<SkVertices> vertices, SkBlendMode mode, const SkPaint& paint,
+                      uint32_t flags = 0);
 
     /**
      Draw a cubic coons patch
@@ -1424,6 +1442,12 @@
     virtual void onDrawVertices(VertexMode, int vertexCount, const SkPoint vertices[],
                                 const SkPoint texs[], const SkColor colors[], SkBlendMode,
                                 const uint16_t indices[], int indexCount, const SkPaint&);
+    virtual void onDrawVerticesObject(sk_sp<SkVertices> vertices, SkBlendMode mode,
+                                      const SkPaint& paint, uint32_t flags);
+    // Subclasses can use this put the vertices object call on the regular draw vertices code path.
+    // This is temporary until we teach recording and other SkCanvas classes about SkVertices.
+    void onDrawVerticesObjectFallback(sk_sp<SkVertices> vertices, SkBlendMode mode,
+                                      const SkPaint& paint, uint32_t flags);
 
     virtual void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[],
                              int count, SkBlendMode, const SkRect* cull, const SkPaint*);
diff --git a/include/core/SkVertices.h b/include/core/SkVertices.h
new file mode 100755
index 0000000..9b94372
--- /dev/null
+++ b/include/core/SkVertices.h
@@ -0,0 +1,141 @@
+/*
+ * 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 SkVertices_DEFINED
+#define SkVertices_DEFINED
+
+#include "SkCanvas.h"
+#include "SkColor.h"
+#include "SkPoint.h"
+#include "SkRect.h"
+#include "SkRefCnt.h"
+
+/**
+ * An immutable set of vertex data that can be used with SkCanvas::drawVertices. Clients are
+ * encouraged to provide a bounds on the vertex positions if they can compute one more cheaply than
+ * looping over the positions.
+ */
+class SkVertices : public SkNVRefCnt<SkVertices> {
+public:
+    static sk_sp<SkVertices> Make(SkCanvas::VertexMode mode,
+                                  std::unique_ptr<const SkPoint[]> positions,
+                                  std::unique_ptr<const SkColor[]> colors, /* optional */
+                                  std::unique_ptr<const SkPoint[]> texs,   /* optional */
+                                  int vertexCnt) {
+        if (!positions || vertexCnt <= 0) {
+            return nullptr;
+        }
+        return sk_sp<SkVertices>(new SkVertices(mode, std::move(positions), std::move(colors),
+                                                std::move(texs), vertexCnt, nullptr, 0, nullptr));
+    }
+
+    static sk_sp<SkVertices> Make(SkCanvas::VertexMode mode,
+                                  std::unique_ptr<const SkPoint[]> positions,
+                                  std::unique_ptr<const SkColor[]> colors, /* optional */
+                                  std::unique_ptr<const SkPoint[]> texs,   /* optional */
+                                  int vertexCnt,
+                                  const SkRect& bounds) {
+        if (!positions || vertexCnt <= 0) {
+            return nullptr;
+        }
+        return sk_sp<SkVertices>(new SkVertices(mode, std::move(positions), std::move(colors),
+                                                std::move(texs), vertexCnt, nullptr, 0, &bounds));
+    }
+
+    static sk_sp<SkVertices> MakeIndexed(SkCanvas::VertexMode mode,
+                                         std::unique_ptr<const SkPoint[]> positions,
+                                         std::unique_ptr<const SkColor[]> colors, /* optional */
+                                         std::unique_ptr<const SkPoint[]> texs,   /* optional */
+                                         int vertexCnt,
+                                         std::unique_ptr<const uint16_t[]> indices,
+                                         int indexCnt) {
+        if (!positions || !indices || vertexCnt <= 0 || indexCnt <= 0) {
+            return nullptr;
+        }
+        return sk_sp<SkVertices>(new SkVertices(mode, std::move(positions), std::move(colors),
+                                                std::move(texs), vertexCnt, std::move(indices),
+                                                indexCnt, nullptr));
+    }
+
+    static sk_sp<SkVertices> MakeIndexed(SkCanvas::VertexMode mode,
+                                         std::unique_ptr<const SkPoint[]> positions,
+                                         std::unique_ptr<const SkColor[]> colors, /* optional */
+                                         std::unique_ptr<const SkPoint[]> texs,   /* optional */
+                                         int vertexCnt,
+                                         std::unique_ptr<const uint16_t[]> indices,
+                                         int indexCnt,
+                                         const SkRect& bounds) {
+        if (!positions || !indices || vertexCnt <= 0 || indexCnt <= 0) {
+            return nullptr;
+        }
+        return sk_sp<SkVertices>(new SkVertices(mode, std::move(positions), std::move(colors),
+                                                std::move(texs), vertexCnt, std::move(indices),
+                                                indexCnt, &bounds));
+    }
+
+    SkCanvas::VertexMode mode() const { return fMode; }
+
+    int vertexCount() const { return fVertexCnt; }
+    bool hasColors() const { return SkToBool(fColors); }
+    bool hasTexCoords() const { return SkToBool(fTexs); }
+    const SkPoint* positions() const { return fPositions.get(); }
+    const SkPoint* texCoords() const { return fTexs.get(); }
+    const SkColor* colors() const { return fColors.get(); }
+
+    bool isIndexed() const { return SkToBool(fIndexCnt); }
+    int indexCount() const { return fIndexCnt; }
+    const uint16_t* indices() const { return fIndices.get(); }
+
+    size_t size() const {
+        return fVertexCnt * (sizeof(SkPoint) * (this->hasTexCoords() ? 2 : 1) + sizeof(SkColor)) +
+               fIndexCnt * sizeof(uint16_t);
+    }
+
+    const SkRect& bounds() const { return fBounds; }
+
+private:
+    SkVertices(SkCanvas::VertexMode mode, std::unique_ptr<const SkPoint[]> positions,
+               std::unique_ptr<const SkColor[]> colors, std::unique_ptr<const SkPoint[]> texs,
+               int vertexCnt, std::unique_ptr<const uint16_t[]> indices, int indexCnt,
+               const SkRect* bounds)
+            : fMode(mode)
+            , fVertexCnt(vertexCnt)
+            , fIndexCnt(indexCnt)
+            , fPositions(std::move(positions))
+            , fColors(std::move(colors))
+            , fTexs(std::move(texs))
+            , fIndices(std::move(indices)) {
+        SkASSERT(SkToBool(fPositions) && SkToBool(fVertexCnt));
+        SkASSERT(SkToBool(fIndices) == SkToBool(fIndexCnt));
+        if (bounds) {
+#ifdef SK_DEBUG
+            fBounds.setBounds(fPositions.get(), fVertexCnt);
+            SkASSERT(bounds->fLeft <= fBounds.fLeft && bounds->fRight >= fBounds.fRight &&
+                     bounds->fTop <= fBounds.fTop && bounds->fBottom >= fBounds.fBottom);
+#endif
+            fBounds = *bounds;
+        } else {
+            fBounds.setBounds(fPositions.get(), fVertexCnt);
+        }
+#ifdef SK_DEBUG
+        for (int i = 0; i < fIndexCnt; ++i) {
+            SkASSERT(fIndices[i] < fVertexCnt);
+        }
+#endif
+    }
+
+    SkCanvas::VertexMode fMode;
+    int fVertexCnt;
+    int fIndexCnt;
+    std::unique_ptr<const SkPoint[]> fPositions;
+    std::unique_ptr<const SkColor[]> fColors;
+    std::unique_ptr<const SkPoint[]> fTexs;
+    std::unique_ptr<const uint16_t[]> fIndices;
+    SkRect fBounds;
+};
+
+#endif
diff --git a/include/gpu/GrRenderTargetContext.h b/include/gpu/GrRenderTargetContext.h
index 403de56..11b1c53 100644
--- a/include/gpu/GrRenderTargetContext.h
+++ b/include/gpu/GrRenderTargetContext.h
@@ -42,6 +42,7 @@
 class SkRRect;
 struct SkRSXform;
 class SkTextBlob;
+class SkVertices;
 
 /**
  * A helper object to orchestrate commands (draws, etc...) for GrSurfaces that are GrRenderTargets.
@@ -230,6 +231,20 @@
                       ColorArrayType = ColorArrayType::kPremulGrColor);
 
     /**
+     * Draws vertices with a paint.
+     *
+     * @param   paint           describes how to color pixels.
+     * @param   viewMatrix      transformation matrix
+     * @param   veritces        specifies the mesh to draw.
+     * @param   flags           A bitfield of options specified by SkCanvas::VerticesFlags.
+     */
+    void drawVertices(const GrClip&,
+                      GrPaint&& paint,
+                      const SkMatrix& viewMatrix,
+                      sk_sp<SkVertices> vertices,
+                      uint32_t flags);
+
+    /**
      * Draws textured sprites from an atlas with a paint. This currently does not support AA for the
      * sprite rectangle edges.
      *
diff --git a/include/utils/SkDumpCanvas.h b/include/utils/SkDumpCanvas.h
index c8f977e..79a49bc 100644
--- a/include/utils/SkDumpCanvas.h
+++ b/include/utils/SkDumpCanvas.h
@@ -9,6 +9,7 @@
 #define SkDumpCanvas_DEFINED
 
 #include "SkCanvas.h"
+#include "SkVertices.h"
 
 /** This class overrides all the draw methods on SkCanvas, and formats them
     as text, and then sends that to a Dumper helper object.
@@ -116,6 +117,10 @@
                         const SkColor colors[], SkBlendMode,
                         const uint16_t indices[], int indexCount,
                         const SkPaint&) override;
+    void onDrawVerticesObject(sk_sp<SkVertices> vertices, SkBlendMode mode, const SkPaint& paint,
+                              uint32_t flags) override {
+        this->onDrawVerticesObjectFallback(std::move(vertices), mode, paint, flags);
+    }
 
     void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override;
     void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
diff --git a/include/utils/SkLuaCanvas.h b/include/utils/SkLuaCanvas.h
index 357ddc8..805c2e1 100644
--- a/include/utils/SkLuaCanvas.h
+++ b/include/utils/SkLuaCanvas.h
@@ -62,6 +62,10 @@
                         const SkColor colors[], SkBlendMode,
                         const uint16_t indices[], int indexCount,
                         const SkPaint&) override;
+    void onDrawVerticesObject(sk_sp<SkVertices> vertices, SkBlendMode mode, const SkPaint& paint,
+                              uint32_t flags) override {
+        this->onDrawVerticesObjectFallback(std::move(vertices), mode, paint, flags);
+    }
 
     void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override;
     void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
diff --git a/include/utils/SkNoDrawCanvas.h b/include/utils/SkNoDrawCanvas.h
index 316a1c0..14b8554 100644
--- a/include/utils/SkNoDrawCanvas.h
+++ b/include/utils/SkNoDrawCanvas.h
@@ -9,6 +9,7 @@
 #define SkNoDrawCanvas_DEFINED
 
 #include "SkCanvas.h"
+#include "SkVertices.h"
 
 struct SkIRect;
 
@@ -67,6 +68,10 @@
                              const SkPaint*) override {}
     void onDrawVertices(VertexMode, int, const SkPoint[], const SkPoint[], const SkColor[],
                         SkBlendMode, const uint16_t[], int, const SkPaint&) override {}
+    void onDrawVerticesObject(sk_sp<SkVertices> vertices, SkBlendMode mode, const SkPaint& paint,
+                              uint32_t flags) override {
+        this->onDrawVerticesObjectFallback(std::move(vertices), mode, paint, flags);
+    }
     void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[],
                      int, SkBlendMode, const SkRect*, const SkPaint*) override {}