| /* |
| * 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); |
| } |
| } |