Make GPU backend triangulate rects such that they are rendered as tri strips rather than tri fans.

Right now when we turn rects into quads we use a vertex order compatible with a tri fan rather than a tri strip.

I wanted it to be the case that the same code could be used to generate a non-indexed mesh for a single rect or indexed using the quad index buffer when batching. Triangle fanning is not available in all APIS (e.g. is emulated in ANGLE and not supported in Metal) so it seems better to use a triangle strip over a fan in the single rect case.


Change-Id: I31eebd794e7328f4b39e3ec3377bf2ec556360ca
Reviewed-on: https://skia-review.googlesource.com/60081
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/gm/beziereffects.cpp b/gm/beziereffects.cpp
index 3a55488..bf53aa2 100644
--- a/gm/beziereffects.cpp
+++ b/gm/beziereffects.cpp
@@ -92,7 +92,7 @@
             return;
         }
         SkRect rect = this->rect();
-        pts[0].setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride);
+        pts[0].setRectTriStrip(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride);
         helper.recordDraw(target, this->gp(), this->makePipeline(target));
     }
 
@@ -267,8 +267,8 @@
             return;
         }
         SkRect rect = this->rect();
-        verts[0].fPosition.setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
-                                      sizeof(Vertex));
+        verts[0].fPosition.setRectTriStrip(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
+                                           sizeof(Vertex));
         for (int v = 0; v < 4; ++v) {
             SkScalar pt3[3] = {verts[v].fPosition.x(), verts[v].fPosition.y(), 1.f};
             fKLM.mapHomogeneousPoints(verts[v].fKLM, pt3, 1);
@@ -481,8 +481,8 @@
             return;
         }
         SkRect rect = this->rect();
-        verts[0].fPosition.setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
-                                      sizeof(Vertex));
+        verts[0].fPosition.setRectTriStrip(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
+                                           sizeof(Vertex));
         fDevToUV.apply<4, sizeof(Vertex), sizeof(SkPoint)>(verts);
         helper.recordDraw(target, this->gp(), this->makePipeline(target));
     }
diff --git a/gm/convexpolyeffect.cpp b/gm/convexpolyeffect.cpp
index 16a185c..16fad46 100644
--- a/gm/convexpolyeffect.cpp
+++ b/gm/convexpolyeffect.cpp
@@ -85,7 +85,8 @@
             return;
         }
 
-        fRect.toQuad(verts);
+        verts->setRectTriStrip(fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom,
+                               sizeof(SkPoint));
 
         helper.recordDraw(
                 target, gp.get(),
diff --git a/include/core/SkPoint.h b/include/core/SkPoint.h
index 4aceff7..7043734 100644
--- a/include/core/SkPoint.h
+++ b/include/core/SkPoint.h
@@ -205,14 +205,6 @@
     void setIRectFan(int l, int t, int r, int b, size_t stride);
 
     // counter-clockwise fan
-    void setRectFan(SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
-        SkPoint* v = this;
-        v[0].set(l, t);
-        v[1].set(l, b);
-        v[2].set(r, b);
-        v[3].set(r, t);
-    }
-
     void setRectFan(SkScalar l, SkScalar t, SkScalar r, SkScalar b, size_t stride) {
         SkASSERT(stride >= sizeof(SkPoint));
 
@@ -222,6 +214,15 @@
         ((SkPoint*)((intptr_t)this + 3 * stride))->set(r, t);
     }
 
+    // tri strip with two counter-clockwise triangles
+    void setRectTriStrip(SkScalar l, SkScalar t, SkScalar r, SkScalar b, size_t stride) {
+        SkASSERT(stride >= sizeof(SkPoint));
+
+        ((SkPoint*)((intptr_t)this + 0 * stride))->set(l, t);
+        ((SkPoint*)((intptr_t)this + 1 * stride))->set(l, b);
+        ((SkPoint*)((intptr_t)this + 2 * stride))->set(r, t);
+        ((SkPoint*)((intptr_t)this + 3 * stride))->set(r, b);
+    }
 
     static void Offset(SkPoint points[], int count, const SkPoint& offset) {
         Offset(points, count, offset.fX, offset.fY);
diff --git a/include/core/SkRSXform.h b/include/core/SkRSXform.h
index 706617e..b11dea7 100644
--- a/include/core/SkRSXform.h
+++ b/include/core/SkRSXform.h
@@ -62,6 +62,7 @@
     void toQuad(const SkSize& size, SkPoint quad[4]) const {
         this->toQuad(size.width(), size.height(), quad);
     }
+    void toTriStrip(SkScalar width, SkScalar height, SkPoint strip[4]) const;
 };
 
 #endif
diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp
index 9be11d5..5b1be84 100644
--- a/src/core/SkMatrix.cpp
+++ b/src/core/SkMatrix.cpp
@@ -1841,3 +1841,17 @@
     quad[3].set(m01 * height + m02, m11 * height + m12);
 #endif
 }
+
+void SkRSXform::toTriStrip(SkScalar width, SkScalar height, SkPoint strip[4]) const {
+    const SkScalar m00 = fSCos;
+    const SkScalar m01 = -fSSin;
+    const SkScalar m02 = fTx;
+    const SkScalar m10 = -m01;
+    const SkScalar m11 = m00;
+    const SkScalar m12 = fTy;
+
+    strip[0].set(m02, m12);
+    strip[1].set(m01 * height + m02, m11 * height + m12);
+    strip[2].set(m00 * width + m02, m10 * width + m12);
+    strip[3].set(m00 * width + m01 * height + m02, m10 * width + m11 * height + m12);
+}
diff --git a/src/core/SkMatrixPriv.h b/src/core/SkMatrixPriv.h
index ee6f8a1..47a1d5f 100644
--- a/src/core/SkMatrixPriv.h
+++ b/src/core/SkMatrixPriv.h
@@ -66,7 +66,7 @@
         }
     }
 
-    static void SetMappedRectFan(const SkMatrix& mx, const SkRect& rect, SkPoint quad[4]) {
+    static void SetMappedRectTriStrip(const SkMatrix& mx, const SkRect& rect, SkPoint quad[4]) {
         SkMatrix::TypeMask tm = mx.getType();
         SkScalar l = rect.fLeft;
         SkScalar t = rect.fTop;
@@ -88,12 +88,9 @@
                 r = sx * r + tx;
                 b = sy * b + ty;
             }
-            quad[0].set(l, t);
-            quad[1].set(l, b);
-            quad[2].set(r, b);
-            quad[3].set(r, t);
+            quad[0].setRectTriStrip(l, t, r, b, sizeof(SkPoint));
         } else {
-            quad[0].setRectFan(l, t, r, b);
+            quad[0].setRectTriStrip(l, t, r, b, sizeof(SkPoint));
             mx.mapPoints(quad, quad, 4);
         }
     }
diff --git a/src/gpu/GrQuad.h b/src/gpu/GrQuad.h
index 3a202c6..3ea170c 100644
--- a/src/gpu/GrQuad.h
+++ b/src/gpu/GrQuad.h
@@ -13,7 +13,8 @@
 #include "SkMatrixPriv.h"
 
 /**
- * GrQuad is a collection of 4 points which can be used to represent an arbitrary quadrilateral
+ * GrQuad is a collection of 4 points which can be used to represent an arbitrary quadrilateral. The
+ * points make a triangle strip with CCW triangles (top-left, bottom-left, top-right, bottom-right).
  */
 class GrQuad {
 public:
@@ -28,7 +29,7 @@
     }
 
     void set(const SkRect& rect) {
-        fPoints->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
+        fPoints->setRectTriStrip(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, sizeof(SkPoint));
     }
 
     void map(const SkMatrix& matrix) {
@@ -36,7 +37,7 @@
     }
 
     void setFromMappedRect(const SkRect& rect, const SkMatrix& matrix) {
-        SkMatrixPriv::SetMappedRectFan(matrix, rect, fPoints);
+        SkMatrixPriv::SetMappedRectTriStrip(matrix, rect, fPoints);
     }
 
     const GrQuad& operator=(const GrQuad& that) {
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 49e7283..9a61154 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -17,6 +17,7 @@
 #include "GrGpuResourcePriv.h"
 #include "GrOpList.h"
 #include "GrPathRenderer.h"
+#include "GrQuad.h"
 #include "GrRenderTarget.h"
 #include "GrRenderTargetContextPriv.h"
 #include "GrResourceProvider.h"
@@ -509,12 +510,12 @@
                     return;
                 }
                 // Does the rect bound the RT?
-                SkPoint srcSpaceRTQuad[4];
-                invM.mapRectToQuad(srcSpaceRTQuad, rtRect);
-                if (rect_contains_inclusive(rect, srcSpaceRTQuad[0]) &&
-                    rect_contains_inclusive(rect, srcSpaceRTQuad[1]) &&
-                    rect_contains_inclusive(rect, srcSpaceRTQuad[2]) &&
-                    rect_contains_inclusive(rect, srcSpaceRTQuad[3])) {
+                GrQuad quad;
+                quad.setFromMappedRect(rtRect, invM);
+                if (rect_contains_inclusive(rect, quad.point(0)) &&
+                    rect_contains_inclusive(rect, quad.point(1)) &&
+                    rect_contains_inclusive(rect, quad.point(2)) &&
+                    rect_contains_inclusive(rect, quad.point(3))) {
                     // Will it blend?
                     GrColor clearColor;
                     if (paint.isConstantBlendedColor(&clearColor)) {
diff --git a/src/gpu/GrResourceProvider.cpp b/src/gpu/GrResourceProvider.cpp
index 5139ee3..dfde098 100644
--- a/src/gpu/GrResourceProvider.cpp
+++ b/src/gpu/GrResourceProvider.cpp
@@ -353,7 +353,7 @@
 
 sk_sp<const GrBuffer> GrResourceProvider::createQuadIndexBuffer() {
     GR_STATIC_ASSERT(4 * kMaxQuads <= 65535);
-    static const uint16_t kPattern[] = { 0, 1, 2, 0, 2, 3 };
+    static const uint16_t kPattern[] = { 0, 1, 2, 2, 1, 3 };
     return this->createPatternedIndexBuffer(kPattern, 6, kMaxQuads, 4, fQuadIndexBufferKey);
 }
 
diff --git a/src/gpu/GrResourceProvider.h b/src/gpu/GrResourceProvider.h
index 9bc4fae..c7b2fe3 100644
--- a/src/gpu/GrResourceProvider.h
+++ b/src/gpu/GrResourceProvider.h
@@ -157,7 +157,7 @@
 
     /**
      * Returns an index buffer that can be used to render quads.
-     * Six indices per quad: 0, 1, 2, 0, 2, 3, etc.
+     * Six indices per quad: 0, 1, 2, 2, 1, 3, etc.
      * The max number of quads is the buffer's index capacity divided by 6.
      * Draw with GrPrimitiveType::kTriangles
      * @ return the quad index buffer
diff --git a/src/gpu/ops/GrDashOp.cpp b/src/gpu/ops/GrDashOp.cpp
index 6ff21b7..070d979 100644
--- a/src/gpu/ops/GrDashOp.cpp
+++ b/src/gpu/ops/GrDashOp.cpp
@@ -166,13 +166,13 @@
     SkScalar endDashY = stroke + bloatY;
     vertices[idx].fDashPos = SkPoint::Make(startDashX , startDashY);
     vertices[idx + 1].fDashPos = SkPoint::Make(startDashX, endDashY);
-    vertices[idx + 2].fDashPos = SkPoint::Make(endDashX, endDashY);
-    vertices[idx + 3].fDashPos = SkPoint::Make(endDashX, startDashY);
+    vertices[idx + 2].fDashPos = SkPoint::Make(endDashX, startDashY);
+    vertices[idx + 3].fDashPos = SkPoint::Make(endDashX, endDashY);
 
     vertices[idx].fPos = SkPoint::Make(rect.fLeft, rect.fTop);
     vertices[idx + 1].fPos = SkPoint::Make(rect.fLeft, rect.fBottom);
-    vertices[idx + 2].fPos = SkPoint::Make(rect.fRight, rect.fBottom);
-    vertices[idx + 3].fPos = SkPoint::Make(rect.fRight, rect.fTop);
+    vertices[idx + 2].fPos = SkPoint::Make(rect.fRight, rect.fTop);
+    vertices[idx + 3].fPos = SkPoint::Make(rect.fRight, rect.fBottom);
 
     matrix.mapPointsWithStride(&vertices[idx].fPos, sizeof(T), 4);
 }
@@ -222,8 +222,8 @@
                                   SkPoint* verts) {
     verts[idx] = SkPoint::Make(rect.fLeft, rect.fTop);
     verts[idx + 1] = SkPoint::Make(rect.fLeft, rect.fBottom);
-    verts[idx + 2] = SkPoint::Make(rect.fRight, rect.fBottom);
-    verts[idx + 3] = SkPoint::Make(rect.fRight, rect.fTop);
+    verts[idx + 2] = SkPoint::Make(rect.fRight, rect.fTop);
+    verts[idx + 3] = SkPoint::Make(rect.fRight, rect.fBottom);
     matrix.mapPoints(&verts[idx], 4);
 }
 
diff --git a/src/gpu/ops/GrDrawAtlasOp.cpp b/src/gpu/ops/GrDrawAtlasOp.cpp
index bd5ffbb..950acae 100644
--- a/src/gpu/ops/GrDrawAtlasOp.cpp
+++ b/src/gpu/ops/GrDrawAtlasOp.cpp
@@ -57,9 +57,9 @@
     int paintAlpha = GrColorUnpackA(installedGeo.fColor);
     for (int spriteIndex = 0; spriteIndex < spriteCount; ++spriteIndex) {
         // Transform rect
-        SkPoint quad[4];
+        SkPoint strip[4];
         const SkRect& currRect = rects[spriteIndex];
-        xforms[spriteIndex].toQuad(currRect.width(), currRect.height(), quad);
+        xforms[spriteIndex].toTriStrip(currRect.width(), currRect.height(), strip);
 
         // Copy colors if necessary
         if (colors) {
@@ -79,28 +79,28 @@
         }
 
         // Copy position and uv to verts
-        *(reinterpret_cast<SkPoint*>(currVertex)) = quad[0];
+        *(reinterpret_cast<SkPoint*>(currVertex)) = strip[0];
         *(reinterpret_cast<SkPoint*>(currVertex + texOffset)) =
                 SkPoint::Make(currRect.fLeft, currRect.fTop);
-        bounds.growToInclude(quad[0]);
+        bounds.growToInclude(strip[0]);
         currVertex += vertexStride;
 
-        *(reinterpret_cast<SkPoint*>(currVertex)) = quad[1];
-        *(reinterpret_cast<SkPoint*>(currVertex + texOffset)) =
-                SkPoint::Make(currRect.fRight, currRect.fTop);
-        bounds.growToInclude(quad[1]);
-        currVertex += vertexStride;
-
-        *(reinterpret_cast<SkPoint*>(currVertex)) = quad[2];
-        *(reinterpret_cast<SkPoint*>(currVertex + texOffset)) =
-                SkPoint::Make(currRect.fRight, currRect.fBottom);
-        bounds.growToInclude(quad[2]);
-        currVertex += vertexStride;
-
-        *(reinterpret_cast<SkPoint*>(currVertex)) = quad[3];
+        *(reinterpret_cast<SkPoint*>(currVertex)) = strip[1];
         *(reinterpret_cast<SkPoint*>(currVertex + texOffset)) =
                 SkPoint::Make(currRect.fLeft, currRect.fBottom);
-        bounds.growToInclude(quad[3]);
+        bounds.growToInclude(strip[1]);
+        currVertex += vertexStride;
+
+        *(reinterpret_cast<SkPoint*>(currVertex)) = strip[2];
+        *(reinterpret_cast<SkPoint*>(currVertex + texOffset)) =
+                SkPoint::Make(currRect.fRight, currRect.fTop);
+        bounds.growToInclude(strip[2]);
+        currVertex += vertexStride;
+
+        *(reinterpret_cast<SkPoint*>(currVertex)) = strip[3];
+        *(reinterpret_cast<SkPoint*>(currVertex + texOffset)) =
+                SkPoint::Make(currRect.fRight, currRect.fBottom);
+        bounds.growToInclude(strip[3]);
         currVertex += vertexStride;
     }
 
diff --git a/src/gpu/ops/GrLatticeOp.cpp b/src/gpu/ops/GrLatticeOp.cpp
index 1f98433..fb5b187 100644
--- a/src/gpu/ops/GrLatticeOp.cpp
+++ b/src/gpu/ops/GrLatticeOp.cpp
@@ -130,13 +130,14 @@
             intptr_t patchVerts = verts;
             while (patch.fIter->next(&srcR, &dstR)) {
                 SkPoint* positions = reinterpret_cast<SkPoint*>(verts);
-                positions->setRectFan(dstR.fLeft, dstR.fTop, dstR.fRight, dstR.fBottom,
-                                      vertexStride);
+                positions->setRectTriStrip(dstR.fLeft, dstR.fTop, dstR.fRight, dstR.fBottom,
+                                           vertexStride);
 
                 // Setup local coords
                 static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
                 SkPoint* coords = reinterpret_cast<SkPoint*>(verts + kLocalOffset);
-                coords->setRectFan(srcR.fLeft, srcR.fTop, srcR.fRight, srcR.fBottom, vertexStride);
+                coords->setRectTriStrip(srcR.fLeft, srcR.fTop, srcR.fRight, srcR.fBottom,
+                                        vertexStride);
 
                 static const int kColorOffset = sizeof(SkPoint);
                 GrColor* vertColor = reinterpret_cast<GrColor*>(verts + kColorOffset);
diff --git a/src/gpu/ops/GrNonAAFillRectOp.cpp b/src/gpu/ops/GrNonAAFillRectOp.cpp
index e0a4b21..5ae9bc2 100644
--- a/src/gpu/ops/GrNonAAFillRectOp.cpp
+++ b/src/gpu/ops/GrNonAAFillRectOp.cpp
@@ -72,7 +72,7 @@
                       const GrQuad* localQuad) {
     SkPoint* positions = reinterpret_cast<SkPoint*>(vertices);
 
-    positions->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride);
+    positions->setRectTriStrip(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride);
 
     if (viewMatrix) {
         SkMatrixPriv::MapPointsWithStride(*viewMatrix, positions, vertexStride, kVertsPerRect);
diff --git a/src/gpu/ops/GrOvalOpFactory.cpp b/src/gpu/ops/GrOvalOpFactory.cpp
index 9c20f48..99aadbc 100644
--- a/src/gpu/ops/GrOvalOpFactory.cpp
+++ b/src/gpu/ops/GrOvalOpFactory.cpp
@@ -1326,15 +1326,15 @@
             verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
             verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
 
-            verts[2].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fBottom);
+            verts[2].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fTop);
             verts[2].fColor = color;
-            verts[2].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
+            verts[2].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
             verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
             verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
 
-            verts[3].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fTop);
+            verts[3].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fBottom);
             verts[3].fColor = color;
-            verts[3].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
+            verts[3].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
             verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
             verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
 
@@ -1549,15 +1549,15 @@
             verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
             verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
 
-            verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
+            verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
             verts[2].fColor = color;
-            verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
-            verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
+            verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
+            verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
 
-            verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
+            verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
             verts[3].fColor = color;
-            verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
-            verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
+            verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
+            verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
 
             verts += kVerticesPerQuad;
         }
diff --git a/src/gpu/ops/GrRegionOp.cpp b/src/gpu/ops/GrRegionOp.cpp
index 1bfed80..10a1171 100644
--- a/src/gpu/ops/GrRegionOp.cpp
+++ b/src/gpu/ops/GrRegionOp.cpp
@@ -34,7 +34,7 @@
     while (!iter.done()) {
         SkRect rect = SkRect::Make(iter.rect());
         SkPoint* position = (SkPoint*)verts;
-        position->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride);
+        position->setRectTriStrip(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride);
 
         static const int kColorOffset = sizeof(SkPoint);
         GrColor* vertColor = reinterpret_cast<GrColor*>(verts + kColorOffset);
diff --git a/src/gpu/ops/GrSmallPathRenderer.cpp b/src/gpu/ops/GrSmallPathRenderer.cpp
index 6d1959f..0aaab9c 100644
--- a/src/gpu/ops/GrSmallPathRenderer.cpp
+++ b/src/gpu/ops/GrSmallPathRenderer.cpp
@@ -7,11 +7,11 @@
  */
 
 #include "GrSmallPathRenderer.h"
-
 #include "GrBuffer.h"
 #include "GrContext.h"
 #include "GrDistanceFieldGenFromVector.h"
 #include "GrDrawOpTest.h"
+#include "GrQuad.h"
 #include "GrResourceProvider.h"
 #include "GrSimpleMeshDrawOpHelper.h"
 #include "SkAutoMalloc.h"
@@ -641,26 +641,26 @@
         // vertex positions
         // TODO make the vertex attributes a struct
         if (fUsesDistanceField && !ctm.hasPerspective()) {
-            SkPoint quad[4];
-            ctm.mapRectToQuad(quad, translatedBounds);
+            GrQuad quad;
+            quad.setFromMappedRect(translatedBounds, ctm);
             intptr_t positionOffset = offset;
             SkPoint* position = (SkPoint*)positionOffset;
-            *position = quad[0];
+            *position = quad.point(0);
             positionOffset += vertexStride;
             position = (SkPoint*)positionOffset;
-            *position = quad[3];
+            *position = quad.point(1);
             positionOffset += vertexStride;
             position = (SkPoint*)positionOffset;
-            *position = quad[2];
+            *position = quad.point(2);
             positionOffset += vertexStride;
             position = (SkPoint*)positionOffset;
-            *position = quad[1];
+            *position = quad.point(3);
         } else {
-            positions->setRectFan(translatedBounds.left(),
-                                  translatedBounds.top(),
-                                  translatedBounds.right(),
-                                  translatedBounds.bottom(),
-                                  vertexStride);
+            positions->setRectTriStrip(translatedBounds.left(),
+                                       translatedBounds.top(),
+                                       translatedBounds.right(),
+                                       translatedBounds.bottom(),
+                                       vertexStride);
         }
 
         // colors
@@ -687,11 +687,11 @@
         textureCoordOffset += vertexStride;
         textureCoords = (uint16_t*)textureCoordOffset;
         textureCoords[0] = r;
-        textureCoords[1] = b;
+        textureCoords[1] = t;
         textureCoordOffset += vertexStride;
         textureCoords = (uint16_t*)textureCoordOffset;
         textureCoords[0] = r;
-        textureCoords[1] = t;
+        textureCoords[1] = b;
     }
 
     void flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) const {
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 291b199..a03b40e 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -344,120 +344,89 @@
             SkDebugf("Could not allocate vertices\n");
             return;
         }
-        sk_sp<const GrBuffer> ibuffer;
+        if (1 == fProxyCnt) {
+            SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::Vertex));
+            for (int i = 0; i < fDraws.count(); ++i) {
+                auto vertices = static_cast<TextureGeometryProcessor::Vertex*>(vdata);
+                GrTexture* texture = proxies[0]->priv().peekTexture();
+                float iw = 1.f / texture->width();
+                float ih = 1.f / texture->height();
+                float tl = iw * fDraws[i].fSrcRect.fLeft;
+                float tr = iw * fDraws[i].fSrcRect.fRight;
+                float tt = ih * fDraws[i].fSrcRect.fTop;
+                float tb = ih * fDraws[i].fSrcRect.fBottom;
+                if (proxies[0]->origin() == kBottomLeft_GrSurfaceOrigin) {
+                    tt = 1.f - tt;
+                    tb = 1.f - tb;
+                }
+                vertices[0 + 4 * i].fPosition = fDraws[i].fQuad.points()[0];
+                vertices[0 + 4 * i].fTextureCoords = {tl, tt};
+                vertices[0 + 4 * i].fColor = fDraws[i].fColor;
+                vertices[1 + 4 * i].fPosition = fDraws[i].fQuad.points()[1];
+                vertices[1 + 4 * i].fTextureCoords = {tl, tb};
+                vertices[1 + 4 * i].fColor = fDraws[i].fColor;
+                vertices[2 + 4 * i].fPosition = fDraws[i].fQuad.points()[2];
+                vertices[2 + 4 * i].fTextureCoords = {tr, tt};
+                vertices[2 + 4 * i].fColor = fDraws[i].fColor;
+                vertices[3 + 4 * i].fPosition = fDraws[i].fQuad.points()[3];
+                vertices[3 + 4 * i].fTextureCoords = {tr, tb};
+                vertices[3 + 4 * i].fColor = fDraws[i].fColor;
+            }
+        } else {
+            SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::MultiTextureVertex));
+            GrTexture* textures[kMaxTextures];
+            float iw[kMaxTextures];
+            float ih[kMaxTextures];
+            for (int t = 0; t < fProxyCnt; ++t) {
+                textures[t] = proxies[t]->priv().peekTexture();
+                iw[t] = 1.f / textures[t]->width();
+                ih[t] = 1.f / textures[t]->height();
+            }
+            for (int i = 0; i < fDraws.count(); ++i) {
+                int t = fDraws[i].fTextureIdx;
+                auto vertices = static_cast<TextureGeometryProcessor::MultiTextureVertex*>(vdata);
+                float tl = iw[t] * fDraws[i].fSrcRect.fLeft;
+                float tr = iw[t] * fDraws[i].fSrcRect.fRight;
+                float tt = ih[t] * fDraws[i].fSrcRect.fTop;
+                float tb = ih[t] * fDraws[i].fSrcRect.fBottom;
+                if (proxies[t]->origin() == kBottomLeft_GrSurfaceOrigin) {
+                    tt = 1.f - tt;
+                    tb = 1.f - tb;
+                }
+                vertices[0 + 4 * i].fPosition = fDraws[i].fQuad.points()[0];
+                vertices[0 + 4 * i].fTextureIdx = t;
+                vertices[0 + 4 * i].fTextureCoords = {tl, tt};
+                vertices[0 + 4 * i].fColor = fDraws[i].fColor;
+                vertices[1 + 4 * i].fPosition = fDraws[i].fQuad.points()[1];
+                vertices[1 + 4 * i].fTextureIdx = t;
+                vertices[1 + 4 * i].fTextureCoords = {tl, tb};
+                vertices[1 + 4 * i].fColor = fDraws[i].fColor;
+                vertices[2 + 4 * i].fPosition = fDraws[i].fQuad.points()[2];
+                vertices[2 + 4 * i].fTextureIdx = t;
+                vertices[2 + 4 * i].fTextureCoords = {tr, tt};
+                vertices[2 + 4 * i].fColor = fDraws[i].fColor;
+                vertices[3 + 4 * i].fPosition = fDraws[i].fQuad.points()[3];
+                vertices[3 + 4 * i].fTextureIdx = t;
+                vertices[3 + 4 * i].fTextureCoords = {tr, tb};
+                vertices[3 + 4 * i].fColor = fDraws[i].fColor;
+            }
+        }
+        GrPrimitiveType primitiveType =
+                fDraws.count() > 1 ? GrPrimitiveType::kTriangles : GrPrimitiveType::kTriangleStrip;
+        GrMesh mesh(primitiveType);
         if (fDraws.count() > 1) {
-            ibuffer = target->resourceProvider()->refQuadIndexBuffer();
+            sk_sp<const GrBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
             if (!ibuffer) {
                 SkDebugf("Could not allocate quad indices\n");
                 return;
             }
-            if (1 == fProxyCnt) {
-                SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::Vertex));
-                for (int i = 0; i < fDraws.count(); ++i) {
-                    auto vertices = static_cast<TextureGeometryProcessor::Vertex*>(vdata);
-                    GrTexture* texture = proxies[0]->priv().peekTexture();
-                    float iw = 1.f / texture->width();
-                    float ih = 1.f / texture->height();
-                    float tl = iw * fDraws[i].fSrcRect.fLeft;
-                    float tr = iw * fDraws[i].fSrcRect.fRight;
-                    float tt = ih * fDraws[i].fSrcRect.fTop;
-                    float tb = ih * fDraws[i].fSrcRect.fBottom;
-                    if (proxies[0]->origin() == kBottomLeft_GrSurfaceOrigin) {
-                        tt = 1.f - tt;
-                        tb = 1.f - tb;
-                    }
-                    vertices[0 + 4 * i].fPosition = fDraws[i].fQuad.points()[0];
-                    vertices[0 + 4 * i].fTextureCoords = {tl, tt};
-                    vertices[0 + 4 * i].fColor = fDraws[i].fColor;
-                    vertices[1 + 4 * i].fPosition = fDraws[i].fQuad.points()[1];
-                    vertices[1 + 4 * i].fTextureCoords = {tl, tb};
-                    vertices[1 + 4 * i].fColor = fDraws[i].fColor;
-                    vertices[2 + 4 * i].fPosition = fDraws[i].fQuad.points()[2];
-                    vertices[2 + 4 * i].fTextureCoords = {tr, tb};
-                    vertices[2 + 4 * i].fColor = fDraws[i].fColor;
-                    vertices[3 + 4 * i].fPosition = fDraws[i].fQuad.points()[3];
-                    vertices[3 + 4 * i].fTextureCoords = {tr, tt};
-                    vertices[3 + 4 * i].fColor = fDraws[i].fColor;
-                }
-            } else {
-                SkASSERT(gp->getVertexStride() ==
-                         sizeof(TextureGeometryProcessor::MultiTextureVertex));
-                GrTexture* textures[kMaxTextures];
-                float iw[kMaxTextures];
-                float ih[kMaxTextures];
-                for (int t = 0; t < fProxyCnt; ++t) {
-                    textures[t] = proxies[t]->priv().peekTexture();
-                    iw[t] = 1.f / textures[t]->width();
-                    ih[t] = 1.f / textures[t]->height();
-                }
-                for (int i = 0; i < fDraws.count(); ++i) {
-                    int t = fDraws[i].fTextureIdx;
-                    auto vertices =
-                            static_cast<TextureGeometryProcessor::MultiTextureVertex*>(vdata);
-                    float tl = iw[t] * fDraws[i].fSrcRect.fLeft;
-                    float tr = iw[t] * fDraws[i].fSrcRect.fRight;
-                    float tt = ih[t] * fDraws[i].fSrcRect.fTop;
-                    float tb = ih[t] * fDraws[i].fSrcRect.fBottom;
-                    if (proxies[t]->origin() == kBottomLeft_GrSurfaceOrigin) {
-                        tt = 1.f - tt;
-                        tb = 1.f - tb;
-                    }
-                    vertices[0 + 4 * i].fPosition = fDraws[i].fQuad.points()[0];
-                    vertices[0 + 4 * i].fTextureIdx = t;
-                    vertices[0 + 4 * i].fTextureCoords = {tl, tt};
-                    vertices[0 + 4 * i].fColor = fDraws[i].fColor;
-                    vertices[1 + 4 * i].fPosition = fDraws[i].fQuad.points()[1];
-                    vertices[1 + 4 * i].fTextureIdx = t;
-                    vertices[1 + 4 * i].fTextureCoords = {tl, tb};
-                    vertices[1 + 4 * i].fColor = fDraws[i].fColor;
-                    vertices[2 + 4 * i].fPosition = fDraws[i].fQuad.points()[2];
-                    vertices[2 + 4 * i].fTextureIdx = t;
-                    vertices[2 + 4 * i].fTextureCoords = {tr, tb};
-                    vertices[2 + 4 * i].fColor = fDraws[i].fColor;
-                    vertices[3 + 4 * i].fPosition = fDraws[i].fQuad.points()[3];
-                    vertices[3 + 4 * i].fTextureIdx = t;
-                    vertices[3 + 4 * i].fTextureCoords = {tr, tt};
-                    vertices[3 + 4 * i].fColor = fDraws[i].fColor;
-                }
-            }
-            GrMesh mesh(GrPrimitiveType::kTriangles);
             mesh.setIndexedPatterned(ibuffer.get(), 6, 4, fDraws.count(),
                                      GrResourceProvider::QuadCountOfQuadBuffer());
-            mesh.setVertexData(vbuffer, vstart);
-            target->draw(gp.get(), pipeline, mesh);
         } else {
-            // If there is only one draw then there can only be one proxy.
-            SkASSERT(1 == fProxyCnt);
-            SkASSERT(gp->getVertexStride() == sizeof(TextureGeometryProcessor::Vertex));
-            auto vertices = static_cast<TextureGeometryProcessor::Vertex*>(vdata);
-            GrTexture* texture = proxies[0]->priv().peekTexture();
-            float iw = 1.f / texture->width();
-            float ih = 1.f / texture->height();
-            float tl = iw * fDraws[0].fSrcRect.fLeft;
-            float tr = iw * fDraws[0].fSrcRect.fRight;
-            float tt = ih * fDraws[0].fSrcRect.fTop;
-            float tb = ih * fDraws[0].fSrcRect.fBottom;
-            if (proxies[0]->origin() == kBottomLeft_GrSurfaceOrigin) {
-                tt = 1.f - tt;
-                tb = 1.f - tb;
-            }
-            vertices[0].fPosition = fDraws[0].fQuad.points()[0];
-            vertices[0].fTextureCoords = {tl, tt};
-            vertices[0].fColor = fDraws[0].fColor;
-            vertices[1].fPosition = fDraws[0].fQuad.points()[3];
-            vertices[1].fTextureCoords = {tr, tt};
-            vertices[1].fColor = fDraws[0].fColor;
-            vertices[2].fPosition = fDraws[0].fQuad.points()[1];
-            vertices[2].fTextureCoords = {tl, tb};
-            vertices[2].fColor = fDraws[0].fColor;
-            vertices[3].fPosition = fDraws[0].fQuad.points()[2];
-            vertices[3].fTextureCoords = {tr, tb};
-            vertices[3].fColor = fDraws[0].fColor;
-            GrMesh mesh(GrPrimitiveType::kTriangleStrip);
             mesh.setNonIndexedNonInstanced(4);
-            mesh.setVertexData(vbuffer, vstart);
-            target->draw(gp.get(), pipeline, mesh);
         }
+        mesh.setVertexData(vbuffer, vstart);
+        target->draw(gp.get(), pipeline, mesh);
     }
 
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
diff --git a/src/gpu/text/GrAtlasTextBlob.cpp b/src/gpu/text/GrAtlasTextBlob.cpp
index b88ed93..5d27b3e 100644
--- a/src/gpu/text/GrAtlasTextBlob.cpp
+++ b/src/gpu/text/GrAtlasTextBlob.cpp
@@ -123,14 +123,14 @@
 
         // V2
         position = reinterpret_cast<SkPoint*>(vertex);
-        position->set(positions.fRight, positions.fBottom);
+        position->set(positions.fRight, positions.fTop);
         colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
         *colorPtr = color;
         vertex += vertexStride;
 
         // V3
         position = reinterpret_cast<SkPoint*>(vertex);
-        position->set(positions.fRight, positions.fTop);
+        position->set(positions.fRight, positions.fBottom);
         colorPtr = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
         *colorPtr = color;
     } else {
@@ -146,12 +146,12 @@
 
         // V2
         position = reinterpret_cast<SkPoint*>(vertex);
-        position->set(positions.fRight, positions.fBottom);
+        position->set(positions.fRight, positions.fTop);
         vertex += vertexStride;
 
         // V3
         position = reinterpret_cast<SkPoint*>(vertex);
-        position->set(positions.fRight, positions.fTop);
+        position->set(positions.fRight, positions.fBottom);
     }
     subRun->appendVertices(vertexStride);
     fGlyphs[subRun->glyphEndIndex()] = glyph;
diff --git a/src/gpu/text/GrAtlasTextBlob_regenInOp.cpp b/src/gpu/text/GrAtlasTextBlob_regenInOp.cpp
index ecd8d52..2d373ad 100644
--- a/src/gpu/text/GrAtlasTextBlob_regenInOp.cpp
+++ b/src/gpu/text/GrAtlasTextBlob_regenInOp.cpp
@@ -112,7 +112,7 @@
     if (regenTexCoords) {
         uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
         textureCoords[0] = u1;
-        textureCoords[1] = v1;
+        textureCoords[1] = v0;
     }
     vertex += vertexStride;
 
@@ -131,7 +131,7 @@
     if (regenTexCoords) {
         uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
         textureCoords[0] = u1;
-        textureCoords[1] = v0;
+        textureCoords[1] = v1;
     }
 }
 
diff --git a/tests/OnFlushCallbackTest.cpp b/tests/OnFlushCallbackTest.cpp
index 3701651..52bed86 100644
--- a/tests/OnFlushCallbackTest.cpp
+++ b/tests/OnFlushCallbackTest.cpp
@@ -128,13 +128,14 @@
         indices[0] = 0;
         indices[1] = 1;
         indices[2] = 2;
-        indices[3] = 0;
-        indices[4] = 2;
+        indices[3] = 2;
+        indices[4] = 1;
         indices[5] = 3;
 
         // Setup positions
         SkPoint* position = (SkPoint*) vertices;
-        position->setRectFan(fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom, vertexStride);
+        position->setRectTriStrip(fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom,
+                                  vertexStride);
 
         // Setup vertex colors
         GrColor* color = (GrColor*)((intptr_t)vertices + kColorOffset);
diff --git a/tests/PrimitiveProcessorTest.cpp b/tests/PrimitiveProcessorTest.cpp
index 004ec88..97dc2ac 100644
--- a/tests/PrimitiveProcessorTest.cpp
+++ b/tests/PrimitiveProcessorTest.cpp
@@ -96,7 +96,7 @@
         QuadHelper helper;
         size_t vertexStride = gp->getVertexStride();
         SkPoint* vertices = reinterpret_cast<SkPoint*>(helper.init(target, vertexStride, 1));
-        vertices->setRectFan(0.f, 0.f, 1.f, 1.f, vertexStride);
+        vertices->setRectTriStrip(0.f, 0.f, 1.f, 1.f, vertexStride);
         helper.recordDraw(target, gp.get(),
                           target->makePipeline(0, GrProcessorSet::MakeEmptySet(),
                                                target->detachAppliedClip()));