| /* | 
 |  * Copyright 2012 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/SkCanvas.h" | 
 | #include "include/core/SkMatrix.h" | 
 | #include "include/core/SkPaint.h" | 
 | #include "include/core/SkPathBuilder.h" | 
 | #include "include/core/SkPathEffect.h" | 
 | #include "include/core/SkRect.h" | 
 | #include "include/core/SkRefCnt.h" | 
 | #include "include/core/SkScalar.h" | 
 | #include "include/core/SkSize.h" | 
 | #include "include/core/SkString.h" | 
 | #include "include/core/SkTypes.h" | 
 | #include "include/effects/Sk1DPathEffect.h" | 
 | #include "include/effects/Sk2DPathEffect.h" | 
 | #include "include/effects/SkCornerPathEffect.h" | 
 | #include "include/effects/SkDashPathEffect.h" | 
 | #include "include/effects/SkDiscretePathEffect.h" | 
 | #include "include/effects/SkOpPathEffect.h" | 
 | #include "include/pathops/SkPathOps.h" | 
 |  | 
 | #include <initializer_list> | 
 |  | 
 | namespace skiagm { | 
 |  | 
 | static void compose_pe(SkPaint* paint) { | 
 |     SkPathEffect* pe = paint->getPathEffect(); | 
 |     sk_sp<SkPathEffect> corner = SkCornerPathEffect::Make(25); | 
 |     sk_sp<SkPathEffect> compose; | 
 |     if (pe) { | 
 |         compose = SkPathEffect::MakeCompose(sk_ref_sp(pe), corner); | 
 |     } else { | 
 |         compose = corner; | 
 |     } | 
 |     paint->setPathEffect(compose); | 
 | } | 
 |  | 
 | static void hair_pe(SkPaint* paint) { | 
 |     paint->setStrokeWidth(0); | 
 | } | 
 |  | 
 | static void hair2_pe(SkPaint* paint) { | 
 |     paint->setStrokeWidth(0); | 
 |     compose_pe(paint); | 
 | } | 
 |  | 
 | static void stroke_pe(SkPaint* paint) { | 
 |     paint->setStrokeWidth(12); | 
 |     compose_pe(paint); | 
 | } | 
 |  | 
 | static void dash_pe(SkPaint* paint) { | 
 |     SkScalar inter[] = { 20, 10, 10, 10 }; | 
 |     paint->setStrokeWidth(12); | 
 |     paint->setPathEffect(SkDashPathEffect::Make(inter, SK_ARRAY_COUNT(inter), 0)); | 
 |     compose_pe(paint); | 
 | } | 
 |  | 
 | constexpr int gXY[] = { | 
 | 4, 0, 0, -4, 8, -4, 12, 0, 8, 4, 0, 4 | 
 | }; | 
 |  | 
 | static SkPath scale(const SkPath& path, SkScalar scale) { | 
 |     SkMatrix m; | 
 |     m.setScale(scale, scale); | 
 |     return path.makeTransform(m); | 
 | } | 
 |  | 
 | static void one_d_pe(SkPaint* paint) { | 
 |     SkPathBuilder b; | 
 |     b.moveTo(SkIntToScalar(gXY[0]), SkIntToScalar(gXY[1])); | 
 |     for (unsigned i = 2; i < SK_ARRAY_COUNT(gXY); i += 2) { | 
 |         b.lineTo(SkIntToScalar(gXY[i]), SkIntToScalar(gXY[i+1])); | 
 |     } | 
 |     b.close().offset(SkIntToScalar(-6), 0); | 
 |     SkPath path = scale(b.detach(), 1.5f); | 
 |  | 
 |     paint->setPathEffect(SkPath1DPathEffect::Make(path, SkIntToScalar(21), 0, | 
 |                                                   SkPath1DPathEffect::kRotate_Style)); | 
 |     compose_pe(paint); | 
 | } | 
 |  | 
 | typedef void (*PE_Proc)(SkPaint*); | 
 | constexpr PE_Proc gPE[] = { hair_pe, hair2_pe, stroke_pe, dash_pe, one_d_pe }; | 
 |  | 
 | static void fill_pe(SkPaint* paint) { | 
 |     paint->setStyle(SkPaint::kFill_Style); | 
 |     paint->setPathEffect(nullptr); | 
 | } | 
 |  | 
 | static void discrete_pe(SkPaint* paint) { | 
 |     paint->setPathEffect(SkDiscretePathEffect::Make(10, 4)); | 
 | } | 
 |  | 
 | static sk_sp<SkPathEffect> MakeTileEffect() { | 
 |     SkMatrix m; | 
 |     m.setScale(SkIntToScalar(12), SkIntToScalar(12)); | 
 |  | 
 |     return SkPath2DPathEffect::Make(m, SkPath::Circle(0,0,5)); | 
 | } | 
 |  | 
 | static void tile_pe(SkPaint* paint) { | 
 |     paint->setPathEffect(MakeTileEffect()); | 
 | } | 
 |  | 
 | constexpr PE_Proc gPE2[] = { fill_pe, discrete_pe, tile_pe }; | 
 |  | 
 | class PathEffectGM : public GM { | 
 | public: | 
 |     PathEffectGM() {} | 
 |  | 
 | protected: | 
 |  | 
 |     SkString onShortName() override { | 
 |         return SkString("patheffect"); | 
 |     } | 
 |  | 
 |     SkISize onISize() override { return SkISize::Make(800, 600); } | 
 |  | 
 |     void onDraw(SkCanvas* canvas) override { | 
 |         SkPaint paint; | 
 |         paint.setAntiAlias(true); | 
 |         paint.setStyle(SkPaint::kStroke_Style); | 
 |  | 
 |         SkPath path = SkPath::Polygon({ | 
 |             {20, 20}, | 
 |             {70, 120}, | 
 |             {120, 30}, | 
 |             {170, 80}, | 
 |             {240, 50}, | 
 |         }, false); | 
 |  | 
 |         canvas->save(); | 
 |         for (size_t i = 0; i < SK_ARRAY_COUNT(gPE); i++) { | 
 |             gPE[i](&paint); | 
 |             canvas->drawPath(path, paint); | 
 |             canvas->translate(0, 75); | 
 |         } | 
 |         canvas->restore(); | 
 |  | 
 |         path.reset(); | 
 |         SkRect r = { 0, 0, 250, 120 }; | 
 |         path = SkPathBuilder().addOval(r, SkPathDirection::kCW) | 
 |                               .addRect(r.makeInset(50, 50), SkPathDirection::kCCW) | 
 |                               .detach(); | 
 |  | 
 |         canvas->translate(320, 20); | 
 |         for (size_t i = 0; i < SK_ARRAY_COUNT(gPE2); i++) { | 
 |             gPE2[i](&paint); | 
 |             canvas->drawPath(path, paint); | 
 |             canvas->translate(0, 160); | 
 |         } | 
 |  | 
 |         const SkIRect rect = SkIRect::MakeXYWH(20, 20, 60, 60); | 
 |         for (size_t i = 0; i < SK_ARRAY_COUNT(gPE); i++) { | 
 |             SkPaint p; | 
 |             p.setAntiAlias(true); | 
 |             p.setStyle(SkPaint::kFill_Style); | 
 |             gPE[i](&p); | 
 |             canvas->drawIRect(rect, p); | 
 |             canvas->translate(75, 0); | 
 |         } | 
 |     } | 
 |  | 
 | private: | 
 |     using INHERITED = GM; | 
 | }; | 
 |  | 
 | DEF_GM( return new PathEffectGM; ) | 
 |  | 
 | }  // namespace skiagm | 
 |  | 
 | ////////////////////////////////////////////////////////////////////////////// | 
 |  | 
 | class ComboPathEfectsGM : public skiagm::GM { | 
 | public: | 
 |     ComboPathEfectsGM() {} | 
 |  | 
 | protected: | 
 |  | 
 |     SkString onShortName() override { | 
 |         return SkString("combo-patheffects"); | 
 |     } | 
 |  | 
 |     SkISize onISize() override { return SkISize::Make(360, 630); } | 
 |  | 
 |     void onDraw(SkCanvas* canvas) override { | 
 |         SkPath path0 = SkPath::Circle(100, 100, 60), | 
 |                path1 = SkPathBuilder().moveTo(20, 20) | 
 |                                       .cubicTo(20, 180, 140, 0, 140, 140) | 
 |                                       .detach(); | 
 |  | 
 |         sk_sp<SkPathEffect> effects[] = { | 
 |             nullptr, | 
 |             SkStrokePathEffect::Make(20, SkPaint::kRound_Join, SkPaint::kRound_Cap, 0), | 
 |             SkMergePathEffect::Make(nullptr, | 
 |                                     SkStrokePathEffect::Make(20, SkPaint::kRound_Join, | 
 |                                                              SkPaint::kRound_Cap, 0), | 
 |                                     kDifference_SkPathOp), | 
 |             SkMergePathEffect::Make(SkMatrixPathEffect::MakeTranslate(50, 30), | 
 |                                     SkStrokePathEffect::Make(20, SkPaint::kRound_Join, | 
 |                                                              SkPaint::kRound_Cap, 0), | 
 |                                     kReverseDifference_SkPathOp), | 
 |         }; | 
 |  | 
 |         SkPaint wireframe; | 
 |         wireframe.setStyle(SkPaint::kStroke_Style); | 
 |         wireframe.setAntiAlias(true); | 
 |  | 
 |         SkPaint paint; | 
 |         paint.setColor(0xFF8888FF); | 
 |         paint.setAntiAlias(true); | 
 |  | 
 |         for (const SkPath& path : { path0, path1 }) { | 
 |             canvas->save(); | 
 |             for (const sk_sp<SkPathEffect>& pe : effects) { | 
 |                 paint.setPathEffect(pe); | 
 |                 canvas->drawPath(path, paint); | 
 |                 canvas->drawPath(path, wireframe); | 
 |  | 
 |                 canvas->translate(0, 150); | 
 |             } | 
 |             canvas->restore(); | 
 |             canvas->translate(180, 0); | 
 |         } | 
 |     } | 
 |  | 
 | private: | 
 |     using INHERITED = GM; | 
 | }; | 
 | DEF_GM(return new ComboPathEfectsGM;) | 
 |  | 
 | #include "include/effects/SkStrokeAndFillPathEffect.h" | 
 |  | 
 | // Test that we can replicate SkPaint::kStrokeAndFill_Style | 
 | // with a patheffect. We expect the 2nd and 3rd columns to draw the same. | 
 | DEF_SIMPLE_GM(stroke_and_fill_patheffect, canvas, 900, 450) { | 
 |     const float kStrokeWidth = 20; | 
 |  | 
 |     typedef SkPath (*Maker)(); | 
 |     const Maker makers[] = { | 
 |         []() { return SkPath::Oval({0, 0, 100, 100}, SkPathDirection::kCW); }, | 
 |         []() { return SkPath::Oval({0, 0, 100, 100}, SkPathDirection::kCCW); }, | 
 |         []() { | 
 |             const SkPoint pts[] = { | 
 |                 {0, 0}, {100, 100}, {0, 100}, {100, 0}, | 
 |             }; | 
 |             return SkPath::Polygon(pts, SK_ARRAY_COUNT(pts), true); | 
 |         }, | 
 |     }; | 
 |  | 
 |     const struct { | 
 |         SkPaint::Style  fStyle; | 
 |         float           fWidth; | 
 |         bool            fUsePE; | 
 |         bool            fExpectStrokeAndFill; | 
 |     } rec[] = { | 
 |         { SkPaint::kStroke_Style,                   0, false, false }, | 
 |         { SkPaint::kFill_Style,                     0,  true, false }, | 
 |         { SkPaint::kStroke_Style,                   0,  true, false }, | 
 |         { SkPaint::kStrokeAndFill_Style, kStrokeWidth, false, true  }, | 
 |         { SkPaint::kStroke_Style,        kStrokeWidth,  true, true  }, | 
 |         { SkPaint::kStrokeAndFill_Style, kStrokeWidth,  true, true  }, | 
 |     }; | 
 |  | 
 |     SkPaint paint; | 
 |     canvas->translate(20, 20); | 
 |     for (auto maker : makers) { | 
 |         const SkPath path = maker(); | 
 |         canvas->save(); | 
 |         for (const auto& r : rec) { | 
 |             paint.setStyle(r.fStyle); | 
 |             paint.setStrokeWidth(r.fWidth); | 
 |             paint.setPathEffect(r.fUsePE ? SkStrokeAndFillPathEffect::Make() : nullptr); | 
 |             paint.setColor(r.fExpectStrokeAndFill ? SK_ColorGRAY : SK_ColorBLACK); | 
 |  | 
 |             canvas->drawPath(path, paint); | 
 |             canvas->translate(150, 0); | 
 |         } | 
 |         canvas->restore(); | 
 |  | 
 |         canvas->translate(0, 150); | 
 |     } | 
 | } |