Fix drawVertices() texture mapping in the presence of a local matrix

Change-Id: I6379f2e7c030d7d1e0fcb2bd6ecc7d81c8a4eec5
Reviewed-on: https://skia-review.googlesource.com/8502
Commit-Queue: Florin Malita <fmalita@chromium.org>
Reviewed-by: Mike Reed <reed@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/gm/vertices.cpp b/gm/vertices.cpp
index eecd9d7..a8f74bf 100644
--- a/gm/vertices.cpp
+++ b/gm/vertices.cpp
@@ -9,19 +9,29 @@
 #include "SkCanvas.h"
 #include "SkColorFilter.h"
 #include "SkGradientShader.h"
+#include "SkLocalMatrixShader.h"
 #include "SkRandom.h"
 #include "SkVertices.h"
 
 static constexpr SkScalar kShaderSize = 40;
-static sk_sp<SkShader> make_shader1() {
+static sk_sp<SkShader> make_shader1(SkScalar shaderScale) {
     const SkColor colors[] = {
         SK_ColorRED, SK_ColorCYAN, SK_ColorGREEN, SK_ColorWHITE,
         SK_ColorMAGENTA, SK_ColorBLUE, SK_ColorYELLOW,
     };
     const SkPoint pts[] = {{kShaderSize / 4, 0}, {3 * kShaderSize / 4, kShaderSize}};
+    const SkMatrix localMatrix = SkMatrix::MakeScale(shaderScale, shaderScale);
 
-    return SkGradientShader::MakeLinear(pts, colors, nullptr, SK_ARRAY_COUNT(colors),
-                                        SkShader::kMirror_TileMode);
+    sk_sp<SkShader> grad = SkGradientShader::MakeLinear(pts, colors, nullptr,
+                                                        SK_ARRAY_COUNT(colors),
+                                                        SkShader::kMirror_TileMode, 0,
+                                                        &localMatrix);
+    // Throw in a couple of local matrix wrappers for good measure.
+    return shaderScale == 1
+        ? grad
+        : sk_make_sp<SkLocalMatrixShader>(
+              sk_make_sp<SkLocalMatrixShader>(std::move(grad), SkMatrix::MakeTrans(-10, 0)),
+              SkMatrix::MakeTrans(10, 0));
 }
 
 static sk_sp<SkShader> make_shader2() {
@@ -44,7 +54,7 @@
 static const int kMeshVertexCnt = 9;
 
 static void fill_mesh(SkPoint pts[kMeshVertexCnt], SkPoint texs[kMeshVertexCnt],
-                      SkColor colors[kMeshVertexCnt]) {
+                      SkColor colors[kMeshVertexCnt], SkScalar shaderScale) {
     pts[0].set(0, 0);
     pts[1].set(kMeshSize / 2, 3);
     pts[2].set(kMeshSize, 0);
@@ -55,15 +65,16 @@
     pts[7].set(kMeshSize / 2, kMeshSize - 3);
     pts[8].set(kMeshSize, kMeshSize);
 
+    const auto shaderSize = kShaderSize * shaderScale;
     texs[0].set(0, 0);
-    texs[1].set(kShaderSize / 2, 0);
-    texs[2].set(kShaderSize, 0);
-    texs[3].set(0, kShaderSize / 2);
-    texs[4].set(kShaderSize / 2, kShaderSize / 2);
-    texs[5].set(kShaderSize, kShaderSize / 2);
-    texs[6].set(0, kShaderSize);
-    texs[7].set(kShaderSize / 2, kShaderSize);
-    texs[8].set(kShaderSize, kShaderSize);
+    texs[1].set(shaderSize / 2, 0);
+    texs[2].set(shaderSize, 0);
+    texs[3].set(0, shaderSize / 2);
+    texs[4].set(shaderSize / 2, shaderSize / 2);
+    texs[5].set(shaderSize, shaderSize / 2);
+    texs[6].set(0, shaderSize);
+    texs[7].set(shaderSize / 2, shaderSize);
+    texs[8].set(shaderSize, shaderSize);
 
     SkRandom rand;
     for (size_t i = 0; i < kMeshVertexCnt; ++i) {
@@ -80,15 +91,17 @@
     sk_sp<SkColorFilter>    fColorFilter;
     sk_sp<SkVertices>       fVertices;
     bool                    fUseObject;
+    SkScalar                fShaderScale;
 
 public:
-    VerticesGM(bool useObject) : fUseObject(useObject) {}
+    VerticesGM(bool useObject, SkScalar shaderScale = 1)
+        : fUseObject(useObject), fShaderScale(shaderScale) {}
 
 protected:
 
     void onOnceBeforeDraw() override {
-        fill_mesh(fPts, fTexs, fColors);
-        fShader1 = make_shader1();
+        fill_mesh(fPts, fTexs, fColors, fShaderScale);
+        fShader1 = make_shader1(fShaderScale);
         fShader2 = make_shader2();
         fColorFilter = make_color_filter();
         if (fUseObject) {
@@ -118,6 +131,9 @@
         if (fUseObject) {
             name.append("_object");
         }
+        if (fShaderScale != 1) {
+            name.append("_scaled_shader");
+        }
         return name;
     }
 
@@ -208,12 +224,13 @@
 
 DEF_GM(return new VerticesGM(true);)
 DEF_GM(return new VerticesGM(false);)
+DEF_GM(return new VerticesGM(false, 1 / kShaderSize);)
 
 static void draw_batching(SkCanvas* canvas, bool useObject) {
     std::unique_ptr<SkPoint[]> pts(new SkPoint[kMeshVertexCnt]);
     std::unique_ptr<SkPoint[]> texs(new SkPoint[kMeshVertexCnt]);
     std::unique_ptr<SkColor[]> colors(new SkColor[kMeshVertexCnt]);
-    fill_mesh(pts.get(), texs.get(), colors.get());
+    fill_mesh(pts.get(), texs.get(), colors.get(), 1);
 
     SkTDArray<SkMatrix> matrices;
     matrices.push()->reset();
@@ -223,7 +240,7 @@
     m->postScale(1.2f, .8f, kMeshSize / 2, kMeshSize / 2);
     m->postTranslate(0, 80);
 
-    auto shader = make_shader1();
+    auto shader = make_shader1(1);
 
     // Triangle fans can't batch so we convert to regular triangles,
     static constexpr int kNumTris = kMeshIndexCnt - 2;
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index 4ec4895..4182ef3 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.cpp
@@ -1855,7 +1855,56 @@
 }
 #endif
 
-static sk_sp<SkShader> MakeTextureShader(const VertState& state, const SkPoint verts[],
+namespace {
+
+// Similar to SkLocalMatrixShader, but composes the local matrix with the CTM (instead
+// of composing with the inherited local matrix):
+//
+//   rec' = {rec.ctm x localMatrix, rec.localMatrix}
+//
+// (as opposed to rec' = {rec.ctm, rec.localMatrix x localMatrix})
+//
+class SkLocalInnerMatrixShader final : public SkShader {
+public:
+    SkLocalInnerMatrixShader(sk_sp<SkShader> proxy, const SkMatrix& localMatrix)
+    : INHERITED(&localMatrix)
+    , fProxyShader(std::move(proxy)) {}
+
+    Factory getFactory() const override {
+        SkASSERT(false);
+        return nullptr;
+    }
+
+protected:
+    void flatten(SkWriteBuffer&) const override {
+        SkASSERT(false);
+    }
+
+    Context* onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const override {
+        SkMatrix adjustedCTM = SkMatrix::Concat(*rec.fMatrix, this->getLocalMatrix());
+        ContextRec newRec(rec);
+        newRec.fMatrix = &adjustedCTM;
+        return fProxyShader->makeContext(newRec, alloc);
+    }
+
+    bool onAppendStages(SkRasterPipeline* p, SkColorSpace* cs, SkArenaAlloc* alloc,
+                        const SkMatrix& ctm, const SkPaint& paint,
+                        const SkMatrix* localM) const override {
+        // We control the shader graph ancestors, so we know there's no local matrix being
+        // injected before this.
+        SkASSERT(!localM);
+
+        SkMatrix adjustedCTM = SkMatrix::Concat(ctm, this->getLocalMatrix());
+        return fProxyShader->appendStages(p, cs, alloc, adjustedCTM, paint);
+    }
+
+private:
+    sk_sp<SkShader> fProxyShader;
+
+    typedef SkShader INHERITED;
+};
+
+sk_sp<SkShader> MakeTextureShader(const VertState& state, const SkPoint verts[],
                                          const SkPoint texs[], const SkPaint& paint,
                                          SkColorSpace* dstColorSpace,
                                          SkArenaAlloc* alloc) {
@@ -1868,9 +1917,12 @@
     if (p0 != p1 || p0 != p2) {
         // Common case (non-collapsed texture coordinates).
         // Map the texture to vertices using a local transform.
+
+        // We cannot use a plain SkLocalMatrix shader, because we need the texture matrix
+        // to compose next to the CTM.
         SkMatrix localMatrix;
         return texture_to_matrix(state, verts, texs, &localMatrix)
-            ? alloc->makeSkSp<SkLocalMatrixShader>(paint.refShader(), localMatrix)
+            ? alloc->makeSkSp<SkLocalInnerMatrixShader>(paint.refShader(), localMatrix)
             : nullptr;
     }
 
@@ -1901,6 +1953,8 @@
     return alloc->makeSkSp<SkColorShader>(SkUnPreMultiply::PMColorToColor(pmColor));
 }
 
+} // anonymous ns
+
 void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count,
                           const SkPoint vertices[], const SkPoint textures[],
                           const SkColor colors[], SkBlendMode bmode,
@@ -1978,7 +2032,7 @@
             //
             static constexpr size_t kAllocSize =
                 sizeof(SkAutoBlitterChoose) + sizeof(SkComposeShader) +
-                SkTMax(sizeof(SkLocalMatrixShader), sizeof(SkColorShader));
+                SkTMax(sizeof(SkLocalInnerMatrixShader), sizeof(SkColorShader));
             char allocBuffer[kAllocSize];
             SkArenaAlloc alloc(allocBuffer);