blob: aabe3deb8599051b7018ef99973ab31d157c5550 [file] [log] [blame]
bsalomon@google.com632151b2012-02-13 15:18:34 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
Ben Wagner7fde8e12019-05-01 17:28:53 -04007
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "gm/gm.h"
9#include "include/core/SkCanvas.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040010#include "include/core/SkMatrix.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkPaint.h"
Mike Reed06d7c9d2020-08-26 12:56:51 -040012#include "include/core/SkPathBuilder.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040013#include "include/core/SkPathEffect.h"
14#include "include/core/SkRect.h"
15#include "include/core/SkRefCnt.h"
16#include "include/core/SkScalar.h"
17#include "include/core/SkSize.h"
18#include "include/core/SkString.h"
19#include "include/core/SkTypes.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050020#include "include/effects/Sk1DPathEffect.h"
21#include "include/effects/Sk2DPathEffect.h"
22#include "include/effects/SkCornerPathEffect.h"
23#include "include/effects/SkDashPathEffect.h"
24#include "include/effects/SkDiscretePathEffect.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040025#include "include/effects/SkOpPathEffect.h"
26#include "include/pathops/SkPathOps.h"
27
28#include <initializer_list>
bsalomon@google.com632151b2012-02-13 15:18:34 +000029
30namespace skiagm {
31
32static void compose_pe(SkPaint* paint) {
33 SkPathEffect* pe = paint->getPathEffect();
reeda4393342016-03-18 11:22:57 -070034 sk_sp<SkPathEffect> corner = SkCornerPathEffect::Make(25);
35 sk_sp<SkPathEffect> compose;
bsalomon@google.com632151b2012-02-13 15:18:34 +000036 if (pe) {
Mike Reeda07741a2017-02-25 22:34:32 -050037 compose = SkPathEffect::MakeCompose(sk_ref_sp(pe), corner);
bsalomon@google.com632151b2012-02-13 15:18:34 +000038 } else {
39 compose = corner;
40 }
reeda4393342016-03-18 11:22:57 -070041 paint->setPathEffect(compose);
bsalomon@google.com632151b2012-02-13 15:18:34 +000042}
43
44static void hair_pe(SkPaint* paint) {
45 paint->setStrokeWidth(0);
46}
47
48static void hair2_pe(SkPaint* paint) {
49 paint->setStrokeWidth(0);
50 compose_pe(paint);
51}
52
53static void stroke_pe(SkPaint* paint) {
54 paint->setStrokeWidth(12);
55 compose_pe(paint);
56}
57
58static void dash_pe(SkPaint* paint) {
59 SkScalar inter[] = { 20, 10, 10, 10 };
60 paint->setStrokeWidth(12);
reeda4393342016-03-18 11:22:57 -070061 paint->setPathEffect(SkDashPathEffect::Make(inter, SK_ARRAY_COUNT(inter), 0));
bsalomon@google.com632151b2012-02-13 15:18:34 +000062 compose_pe(paint);
63}
64
mtkleindbfd7ab2016-09-01 11:24:54 -070065constexpr int gXY[] = {
bsalomon@google.com632151b2012-02-13 15:18:34 +0000664, 0, 0, -4, 8, -4, 12, 0, 8, 4, 0, 4
67};
68
Mike Reed06d7c9d2020-08-26 12:56:51 -040069static SkPath scale(const SkPath& path, SkScalar scale) {
bsalomon@google.com632151b2012-02-13 15:18:34 +000070 SkMatrix m;
71 m.setScale(scale, scale);
Mike Reed06d7c9d2020-08-26 12:56:51 -040072 return path.makeTransform(m);
bsalomon@google.com632151b2012-02-13 15:18:34 +000073}
74
75static void one_d_pe(SkPaint* paint) {
Mike Reed06d7c9d2020-08-26 12:56:51 -040076 SkPathBuilder b;
77 b.moveTo(SkIntToScalar(gXY[0]), SkIntToScalar(gXY[1]));
78 for (unsigned i = 2; i < SK_ARRAY_COUNT(gXY); i += 2) {
79 b.lineTo(SkIntToScalar(gXY[i]), SkIntToScalar(gXY[i+1]));
80 }
81 b.close().offset(SkIntToScalar(-6), 0);
82 SkPath path = scale(b.detach(), 1.5f);
rmistry@google.comae933ce2012-08-23 18:19:56 +000083
reeda4393342016-03-18 11:22:57 -070084 paint->setPathEffect(SkPath1DPathEffect::Make(path, SkIntToScalar(21), 0,
85 SkPath1DPathEffect::kRotate_Style));
bsalomon@google.com632151b2012-02-13 15:18:34 +000086 compose_pe(paint);
87}
88
89typedef void (*PE_Proc)(SkPaint*);
mtkleindbfd7ab2016-09-01 11:24:54 -070090constexpr PE_Proc gPE[] = { hair_pe, hair2_pe, stroke_pe, dash_pe, one_d_pe };
bsalomon@google.com632151b2012-02-13 15:18:34 +000091
92static void fill_pe(SkPaint* paint) {
93 paint->setStyle(SkPaint::kFill_Style);
halcanary96fcdcc2015-08-27 07:41:13 -070094 paint->setPathEffect(nullptr);
bsalomon@google.com632151b2012-02-13 15:18:34 +000095}
96
97static void discrete_pe(SkPaint* paint) {
reeda4393342016-03-18 11:22:57 -070098 paint->setPathEffect(SkDiscretePathEffect::Make(10, 4));
bsalomon@google.com632151b2012-02-13 15:18:34 +000099}
100
reeda4393342016-03-18 11:22:57 -0700101static sk_sp<SkPathEffect> MakeTileEffect() {
bsalomon@google.com632151b2012-02-13 15:18:34 +0000102 SkMatrix m;
103 m.setScale(SkIntToScalar(12), SkIntToScalar(12));
104
Mike Reed06d7c9d2020-08-26 12:56:51 -0400105 return SkPath2DPathEffect::Make(m, SkPath::Circle(0,0,5));
bsalomon@google.com632151b2012-02-13 15:18:34 +0000106}
107
108static void tile_pe(SkPaint* paint) {
reeda4393342016-03-18 11:22:57 -0700109 paint->setPathEffect(MakeTileEffect());
bsalomon@google.com632151b2012-02-13 15:18:34 +0000110}
111
mtkleindbfd7ab2016-09-01 11:24:54 -0700112constexpr PE_Proc gPE2[] = { fill_pe, discrete_pe, tile_pe };
bsalomon@google.com632151b2012-02-13 15:18:34 +0000113
114class PathEffectGM : public GM {
115public:
116 PathEffectGM() {}
117
118protected:
commit-bot@chromium.orga90c6802014-04-30 13:20:45 +0000119
mtklein36352bf2015-03-25 18:17:31 -0700120 SkString onShortName() override {
bsalomon@google.com632151b2012-02-13 15:18:34 +0000121 return SkString("patheffect");
122 }
123
mtklein36352bf2015-03-25 18:17:31 -0700124 SkISize onISize() override { return SkISize::Make(800, 600); }
bsalomon@google.com632151b2012-02-13 15:18:34 +0000125
mtklein36352bf2015-03-25 18:17:31 -0700126 void onDraw(SkCanvas* canvas) override {
bsalomon@google.com632151b2012-02-13 15:18:34 +0000127 SkPaint paint;
128 paint.setAntiAlias(true);
129 paint.setStyle(SkPaint::kStroke_Style);
130
Mike Reed06d7c9d2020-08-26 12:56:51 -0400131 SkPath path = SkPath::Polygon({
132 {20, 20},
133 {70, 120},
134 {120, 30},
135 {170, 80},
136 {240, 50},
137 }, false);
bsalomon@google.com632151b2012-02-13 15:18:34 +0000138
bsalomon@google.com632151b2012-02-13 15:18:34 +0000139 canvas->save();
Robert Phillips20390c32018-08-17 11:01:03 -0400140 for (size_t i = 0; i < SK_ARRAY_COUNT(gPE); i++) {
bsalomon@google.com632151b2012-02-13 15:18:34 +0000141 gPE[i](&paint);
142 canvas->drawPath(path, paint);
143 canvas->translate(0, 75);
144 }
145 canvas->restore();
146
147 path.reset();
148 SkRect r = { 0, 0, 250, 120 };
Mike Reed06d7c9d2020-08-26 12:56:51 -0400149 path = SkPathBuilder().addOval(r, SkPathDirection::kCW)
150 .addRect(r.makeInset(50, 50), SkPathDirection::kCCW)
151 .detach();
bsalomon@google.com632151b2012-02-13 15:18:34 +0000152
153 canvas->translate(320, 20);
Robert Phillips20390c32018-08-17 11:01:03 -0400154 for (size_t i = 0; i < SK_ARRAY_COUNT(gPE2); i++) {
bsalomon@google.com632151b2012-02-13 15:18:34 +0000155 gPE2[i](&paint);
156 canvas->drawPath(path, paint);
157 canvas->translate(0, 160);
158 }
bsalomon@google.com22f42b72012-03-26 14:36:55 +0000159
Robert Phillips20390c32018-08-17 11:01:03 -0400160 const SkIRect rect = SkIRect::MakeXYWH(20, 20, 60, 60);
161 for (size_t i = 0; i < SK_ARRAY_COUNT(gPE); i++) {
bsalomon@google.com22f42b72012-03-26 14:36:55 +0000162 SkPaint p;
163 p.setAntiAlias(true);
164 p.setStyle(SkPaint::kFill_Style);
165 gPE[i](&p);
166 canvas->drawIRect(rect, p);
167 canvas->translate(75, 0);
168 }
bsalomon@google.com632151b2012-02-13 15:18:34 +0000169 }
170
171private:
John Stiles7571f9e2020-09-02 22:42:33 -0400172 using INHERITED = GM;
bsalomon@google.com632151b2012-02-13 15:18:34 +0000173};
174
Hal Canarye964c182019-01-23 10:22:01 -0500175DEF_GM( return new PathEffectGM; )
bsalomon@google.com632151b2012-02-13 15:18:34 +0000176
John Stilesa6841be2020-08-06 14:11:56 -0400177} // namespace skiagm
Mike Reed0ef539a2018-07-18 13:28:42 -0400178
179//////////////////////////////////////////////////////////////////////////////
Mike Reed0ef539a2018-07-18 13:28:42 -0400180
181class ComboPathEfectsGM : public skiagm::GM {
182public:
183 ComboPathEfectsGM() {}
184
185protected:
186
187 SkString onShortName() override {
188 return SkString("combo-patheffects");
189 }
190
191 SkISize onISize() override { return SkISize::Make(360, 630); }
192
193 void onDraw(SkCanvas* canvas) override {
Mike Reed06d7c9d2020-08-26 12:56:51 -0400194 SkPath path0 = SkPath::Circle(100, 100, 60),
195 path1 = SkPathBuilder().moveTo(20, 20)
196 .cubicTo(20, 180, 140, 0, 140, 140)
197 .detach();
Mike Reed0ef539a2018-07-18 13:28:42 -0400198
199 sk_sp<SkPathEffect> effects[] = {
200 nullptr,
201 SkStrokePathEffect::Make(20, SkPaint::kRound_Join, SkPaint::kRound_Cap, 0),
Mike Reed310f44d2018-07-19 13:47:44 -0400202 SkMergePathEffect::Make(nullptr,
203 SkStrokePathEffect::Make(20, SkPaint::kRound_Join,
204 SkPaint::kRound_Cap, 0),
205 kDifference_SkPathOp),
206 SkMergePathEffect::Make(SkMatrixPathEffect::MakeTranslate(50, 30),
207 SkStrokePathEffect::Make(20, SkPaint::kRound_Join,
208 SkPaint::kRound_Cap, 0),
209 kReverseDifference_SkPathOp),
Mike Reed0ef539a2018-07-18 13:28:42 -0400210 };
211
212 SkPaint wireframe;
213 wireframe.setStyle(SkPaint::kStroke_Style);
214 wireframe.setAntiAlias(true);
215
216 SkPaint paint;
217 paint.setColor(0xFF8888FF);
218 paint.setAntiAlias(true);
219
John Stilesbd3ffa42020-07-30 20:24:57 -0400220 for (const SkPath& path : { path0, path1 }) {
Mike Reed0ef539a2018-07-18 13:28:42 -0400221 canvas->save();
John Stilesbd3ffa42020-07-30 20:24:57 -0400222 for (const sk_sp<SkPathEffect>& pe : effects) {
Mike Reed0ef539a2018-07-18 13:28:42 -0400223 paint.setPathEffect(pe);
224 canvas->drawPath(path, paint);
225 canvas->drawPath(path, wireframe);
226
227 canvas->translate(0, 150);
228 }
229 canvas->restore();
230 canvas->translate(180, 0);
231 }
232 }
233
234private:
John Stiles7571f9e2020-09-02 22:42:33 -0400235 using INHERITED = GM;
Mike Reed0ef539a2018-07-18 13:28:42 -0400236};
237DEF_GM(return new ComboPathEfectsGM;)
238
Mike Reed3e843122020-05-20 09:55:58 -0400239#include "include/effects/SkStrokeAndFillPathEffect.h"
240
241// Test that we can replicate SkPaint::kStrokeAndFill_Style
242// with a patheffect. We expect the 2nd and 3rd columns to draw the same.
243DEF_SIMPLE_GM(stroke_and_fill_patheffect, canvas, 900, 450) {
244 const float kStrokeWidth = 20;
245
Mike Reed74a7a812020-08-02 22:14:43 -0400246 typedef SkPath (*Maker)();
Mike Reed3e843122020-05-20 09:55:58 -0400247 const Maker makers[] = {
Mike Reed74a7a812020-08-02 22:14:43 -0400248 []() { return SkPath::Oval({0, 0, 100, 100}, SkPathDirection::kCW); },
249 []() { return SkPath::Oval({0, 0, 100, 100}, SkPathDirection::kCCW); },
250 []() {
251 const SkPoint pts[] = {
252 {0, 0}, {100, 100}, {0, 100}, {100, 0},
253 };
254 return SkPath::Polygon(pts, SK_ARRAY_COUNT(pts), true);
Mike Reed3e843122020-05-20 09:55:58 -0400255 },
256 };
257
258 const struct {
259 SkPaint::Style fStyle;
260 float fWidth;
261 bool fUsePE;
262 bool fExpectStrokeAndFill;
263 } rec[] = {
264 { SkPaint::kStroke_Style, 0, false, false },
265 { SkPaint::kFill_Style, 0, true, false },
266 { SkPaint::kStroke_Style, 0, true, false },
267 { SkPaint::kStrokeAndFill_Style, kStrokeWidth, false, true },
268 { SkPaint::kStroke_Style, kStrokeWidth, true, true },
269 { SkPaint::kStrokeAndFill_Style, kStrokeWidth, true, true },
270 };
271
272 SkPaint paint;
273 canvas->translate(20, 20);
274 for (auto maker : makers) {
Mike Reed74a7a812020-08-02 22:14:43 -0400275 const SkPath path = maker();
Mike Reed3e843122020-05-20 09:55:58 -0400276 canvas->save();
277 for (const auto& r : rec) {
278 paint.setStyle(r.fStyle);
279 paint.setStrokeWidth(r.fWidth);
280 paint.setPathEffect(r.fUsePE ? SkStrokeAndFillPathEffect::Make() : nullptr);
281 paint.setColor(r.fExpectStrokeAndFill ? SK_ColorGRAY : SK_ColorBLACK);
282
283 canvas->drawPath(path, paint);
284 canvas->translate(150, 0);
285 }
286 canvas->restore();
287
288 canvas->translate(0, 150);
289 }
290}