[gm] Add GMs that apply a gradient to a path

Add two gradient shader GMs that apply the shader paint to a Bezier
curve. This is to test the correctness of atlas path renders.

Bug: b/280927575
Change-Id: I63bc0bc568dc1c2cdade49d7837b1c37653041a4
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/712677
Reviewed-by: Brian Osman <brianosman@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Arman Uguray <armansito@google.com>
diff --git a/gm/cubicpaths.cpp b/gm/cubicpaths.cpp
index 54941d1..ad3f252 100644
--- a/gm/cubicpaths.cpp
+++ b/gm/cubicpaths.cpp
@@ -19,6 +19,7 @@
 #include "include/core/SkString.h"
 #include "include/core/SkTypeface.h"
 #include "include/core/SkTypes.h"
+#include "include/effects/SkGradientShader.h"
 #include "src/base/SkRandom.h"
 #include "tools/ToolUtils.h"
 
@@ -117,7 +118,6 @@
     using INHERITED = skiagm::GM;
 };
 
-
 class CubicPathGM : public skiagm::GM {
     SkString onShortName() override { return SkString("cubicpath"); }
 
@@ -358,6 +358,132 @@
     }
 };
 
+class CubicPathShaderGM : public skiagm::GM {
+    SkString onShortName() override { return SkString("cubicpath_shader"); }
+
+    SkISize onISize() override { return {1240, 390}; }
+
+    void drawPath(SkPath& path,SkCanvas* canvas,
+                  const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
+                  SkPaint::Style style, SkPathFillType fill,
+                  SkScalar strokeWidth) {
+        const SkScalar s = 50.f;
+        const SkPoint     kPts[] = { { 0, 0 }, { s, s } };
+        const SkScalar    kPos[] = { 0, SK_Scalar1/2, SK_Scalar1 };
+        const SkColor  kColors[] = {0x80F00080, 0xF0F08000, 0x800080F0 };
+
+        path.setFillType(fill);
+
+        SkPaint paint;
+        paint.setStrokeCap(cap);
+        paint.setStrokeWidth(strokeWidth);
+        paint.setStrokeJoin(join);
+        paint.setShader(SkGradientShader::MakeLinear(kPts, kColors, kPos,
+                        std::size(kColors), SkTileMode::kClamp));
+        paint.setStyle(style);
+        canvas->save();
+        canvas->clipRect(clip);
+        canvas->drawPath(path, paint);
+        canvas->restore();
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        struct FillAndName {
+            SkPathFillType fFill;
+            const char*      fName;
+        };
+        constexpr FillAndName gFills[] = {
+            {SkPathFillType::kWinding, "Winding"},
+            {SkPathFillType::kEvenOdd, "Even / Odd"},
+            {SkPathFillType::kInverseWinding, "Inverse Winding"},
+            {SkPathFillType::kInverseEvenOdd, "Inverse Even / Odd"},
+        };
+        struct StyleAndName {
+            SkPaint::Style fStyle;
+            const char*    fName;
+        };
+        constexpr StyleAndName gStyles[] = {
+            {SkPaint::kFill_Style, "Fill"},
+            {SkPaint::kStroke_Style, "Stroke"},
+            {SkPaint::kStrokeAndFill_Style, "Stroke And Fill"},
+        };
+        struct CapAndName {
+            SkPaint::Cap  fCap;
+            SkPaint::Join fJoin;
+            const char*   fName;
+        };
+        constexpr CapAndName gCaps[] = {
+            {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"},
+            {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"},
+            {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"}
+        };
+        struct PathAndName {
+            SkPath      fPath;
+            const char* fName;
+        };
+        PathAndName path;
+        path.fPath.moveTo(25*SK_Scalar1, 10*SK_Scalar1);
+        path.fPath.cubicTo(40*SK_Scalar1, 20*SK_Scalar1,
+                           60*SK_Scalar1, 20*SK_Scalar1,
+                           75*SK_Scalar1, 10*SK_Scalar1);
+        path.fName = "moveTo-cubic";
+
+        SkPaint titlePaint;
+        titlePaint.setColor(SK_ColorBLACK);
+        titlePaint.setAntiAlias(true);
+        SkFont     font(ToolUtils::create_portable_typeface(), 15);
+        const char title[] = "Cubic Drawn Into Rectangle Clips With "
+                             "Indicated Style, Fill and Linecaps, with stroke width 10";
+        canvas->drawString(title, 20, 20, font, titlePaint);
+
+        SkRandom rand;
+        SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
+        canvas->save();
+        canvas->translate(10 * SK_Scalar1, 30 * SK_Scalar1);
+        canvas->save();
+        for (size_t cap = 0; cap < std::size(gCaps); ++cap) {
+            if (0 < cap) {
+                canvas->translate((rect.width() + 40 * SK_Scalar1) * std::size(gStyles), 0);
+            }
+            canvas->save();
+            for (size_t fill = 0; fill < std::size(gFills); ++fill) {
+                if (0 < fill) {
+                    canvas->translate(0, rect.height() + 40 * SK_Scalar1);
+                }
+                canvas->save();
+                for (size_t style = 0; style < std::size(gStyles); ++style) {
+                    if (0 < style) {
+                        canvas->translate(rect.width() + 40 * SK_Scalar1, 0);
+                    }
+
+                    SkColor color = 0xff007000;
+                    this->drawPath(path.fPath, canvas, rect,
+                                    gCaps[cap].fCap, gCaps[cap].fJoin, gStyles[style].fStyle,
+                                    gFills[fill].fFill, SK_Scalar1*10);
+
+                    SkPaint rectPaint;
+                    rectPaint.setColor(SK_ColorBLACK);
+                    rectPaint.setStyle(SkPaint::kStroke_Style);
+                    rectPaint.setStrokeWidth(-1);
+                    rectPaint.setAntiAlias(true);
+                    canvas->drawRect(rect, rectPaint);
+
+                    SkPaint labelPaint;
+                    labelPaint.setColor(color);
+                    font.setSize(10);
+                    canvas->drawString(gStyles[style].fName, 0, rect.height() + 12, font, labelPaint);
+                    canvas->drawString(gFills[fill].fName, 0, rect.height() + 24, font, labelPaint);
+                    canvas->drawString(gCaps[cap].fName, 0, rect.height() + 36, font, labelPaint);
+                }
+                canvas->restore();
+            }
+            canvas->restore();
+        }
+        canvas->restore();
+        canvas->restore();
+    }
+};
+
 DEF_SIMPLE_GM(bug5099, canvas, 50, 50) {
     SkPaint p;
     p.setColor(SK_ColorRED);
@@ -398,6 +524,7 @@
 //////////////////////////////////////////////////////////////////////////////
 
 DEF_GM( return new CubicPathGM; )
+DEF_GM( return new CubicPathShaderGM; )
 DEF_GM( return new CubicClosePathGM; )
 DEF_GM( return new ClippedCubicGM; )
 DEF_GM( return new ClippedCubic2GM; )
diff --git a/gm/shaderpath.cpp b/gm/shaderpath.cpp
new file mode 100644
index 0000000..d188aa6
--- /dev/null
+++ b/gm/shaderpath.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm/gm.h"
+#include "include/core/SkBitmap.h"
+#include "include/core/SkCanvas.h"
+#include "include/core/SkColor.h"
+#include "include/core/SkFont.h"
+#include "include/core/SkFontTypes.h"
+#include "include/core/SkMatrix.h"
+#include "include/core/SkPaint.h"
+#include "include/core/SkPath.h"
+#include "include/core/SkPoint.h"
+#include "include/core/SkScalar.h"
+#include "include/core/SkShader.h"
+#include "include/core/SkSize.h"
+#include "include/core/SkString.h"
+#include "include/core/SkTileMode.h"
+#include "include/core/SkTypes.h"
+#include "include/effects/SkGradientShader.h"
+#include "tools/ToolUtils.h"
+
+#include <string.h>
+
+namespace skiagm {
+
+static void makebm(SkBitmap* bm, int w, int h) {
+    bm->allocN32Pixels(w, h);
+    bm->eraseColor(SK_ColorTRANSPARENT);
+
+    SkCanvas    canvas(*bm);
+    SkScalar    s = SkIntToScalar(std::min(w, h));
+    const SkPoint     kPts0[] = { { 0, 0 }, { s, s } };
+    const SkPoint     kPts1[] = { { s/2, 0 }, { s/2, s } };
+    const SkScalar    kPos[] = { 0, SK_Scalar1/2, SK_Scalar1 };
+    const SkColor kColors0[] = {0x80F00080, 0xF0F08000, 0x800080F0 };
+    const SkColor kColors1[] = {0xF08000F0, 0x8080F000, 0xF000F080 };
+
+
+    SkPaint     paint;
+
+    paint.setShader(SkGradientShader::MakeLinear(kPts0, kColors0, kPos,
+                    std::size(kColors0), SkTileMode::kClamp));
+    canvas.drawPaint(paint);
+    paint.setShader(SkGradientShader::MakeLinear(kPts1, kColors1, kPos,
+                    std::size(kColors1), SkTileMode::kClamp));
+    canvas.drawPaint(paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct LabeledMatrix {
+    SkMatrix    fMatrix;
+    const char* fLabel;
+};
+
+constexpr int kPointSize = 300;
+
+class ShaderPathGM : public GM {
+public:
+    ShaderPathGM() {
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+protected:
+
+    SkString onShortName() override {
+        return SkString("shaderpath");
+    }
+
+    SkISize onISize() override { return SkISize::Make(820, 930); }
+
+    void onOnceBeforeDraw() override {
+        makebm(&fBmp, kPointSize / 4, kPointSize / 4);
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+
+        SkPaint bmpPaint;
+        bmpPaint.setAntiAlias(true);
+        bmpPaint.setAlphaf(0.5f);
+        SkSamplingOptions sampling(SkFilterMode::kLinear);
+
+        canvas->drawImage(fBmp.asImage(), 5.f, 5.f, sampling, &bmpPaint);
+
+        SkPaint outlinePaint;
+        outlinePaint.setStyle(SkPaint::kStroke_Style);
+        outlinePaint.setStrokeWidth(0.f);
+
+        canvas->translate(15.f, 15.f);
+        canvas->scale(2.f, 2.f);
+
+        constexpr SkTileMode kTileModes[] = {
+            SkTileMode::kRepeat,
+            SkTileMode::kMirror,
+        };
+
+        // position the baseline of the first path
+        canvas->translate(0.f, 2.25);
+
+        SkPath path;
+        path.moveTo(0, 40).cubicTo(10, 70, 20, 10, 30, 40);
+
+        canvas->save();
+        int i = 0;
+        for (size_t tm0 = 0; tm0 < std::size(kTileModes); ++tm0) {
+            for (size_t tm1 = 0; tm1 < std::size(kTileModes); ++tm1) {
+                SkMatrix localM;
+                localM.setTranslate(5.f, 5.f);
+                localM.postRotate(20);
+                localM.postScale(1.15f, .85f);
+
+                SkPaint fillPaint;
+                fillPaint.setAntiAlias(true);
+                fillPaint.setShader(fBmp.makeShader(kTileModes[tm0], kTileModes[tm1],
+                                                    sampling, localM));
+
+                canvas->drawPath(path, fillPaint);
+                canvas->drawPath(path, outlinePaint);
+                canvas->translate(50.f, 0.f);
+                ++i;
+                if (!(i % 2)) {
+                    canvas->restore();
+                    canvas->translate(0, 22.5f);
+                    canvas->save();
+                }
+            }
+        }
+        canvas->restore();
+    }
+
+private:
+    SkBitmap fBmp;
+    using INHERITED = GM;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return new ShaderPathGM; )
+}  // namespace skiagm
diff --git a/gn/gm.gni b/gn/gm.gni
index d1ee22a..e88cbec 100644
--- a/gn/gm.gni
+++ b/gn/gm.gni
@@ -332,6 +332,7 @@
   "$_gm/scaledrects.cpp",
   "$_gm/scaledstrokes.cpp",
   "$_gm/shadermaskfilter.cpp",
+  "$_gm/shaderpath.cpp",
   "$_gm/shadertext3.cpp",
   "$_gm/shadowutils.cpp",
   "$_gm/shallowgradient.cpp",