/* * 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 namespace skiagm { static void compose_pe(SkPaint* paint) { SkPathEffect* pe = paint->getPathEffect(); sk_sp corner = SkCornerPathEffect::Make(25); sk_sp 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 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 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& 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); } }