1 /*
2 * Copyright 2011 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 */
7
8 #include "gm/gm.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkPaint.h"
12 #include "include/core/SkPath.h"
13 #include "include/core/SkPathEffect.h"
14 #include "include/core/SkPoint.h"
15 #include "include/core/SkRect.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"
20 #include "include/effects/SkDashPathEffect.h"
21 #include "include/private/SkFloatBits.h"
22 #include "include/utils/SkParsePath.h"
23 #include "include/utils/SkRandom.h"
24 #include "tools/ToolUtils.h"
25
26 #include <string.h>
27
28 #define W 400
29 #define H 400
30 #define N 50
31
32 constexpr SkScalar SW = SkIntToScalar(W);
33 constexpr SkScalar SH = SkIntToScalar(H);
34
rnd_rect(SkRect * r,SkPaint * paint,SkRandom & rand)35 static void rnd_rect(SkRect* r, SkPaint* paint, SkRandom& rand) {
36 SkScalar x = rand.nextUScalar1() * W;
37 SkScalar y = rand.nextUScalar1() * H;
38 SkScalar w = rand.nextUScalar1() * (W >> 2);
39 SkScalar h = rand.nextUScalar1() * (H >> 2);
40 SkScalar hoffset = rand.nextSScalar1();
41 SkScalar woffset = rand.nextSScalar1();
42
43 r->set(x, y, x + w, y + h);
44 r->offset(-w/2 + woffset, -h/2 + hoffset);
45
46 paint->setColor(rand.nextU());
47 paint->setAlphaf(1.0f);
48 }
49
50
51 class StrokesGM : public skiagm::GM {
52 public:
StrokesGM()53 StrokesGM() {}
54
55 protected:
56
onShortName()57 SkString onShortName() override {
58 return SkString("strokes_round");
59 }
60
onISize()61 SkISize onISize() override {
62 return SkISize::Make(W, H*2);
63 }
64
onDraw(SkCanvas * canvas)65 void onDraw(SkCanvas* canvas) override {
66 SkPaint paint;
67 paint.setStyle(SkPaint::kStroke_Style);
68 paint.setStrokeWidth(SkIntToScalar(9)/2);
69
70 for (int y = 0; y < 2; y++) {
71 paint.setAntiAlias(!!y);
72 SkAutoCanvasRestore acr(canvas, true);
73 canvas->translate(0, SH * y);
74 canvas->clipRect(SkRect::MakeLTRB(
75 SkIntToScalar(2), SkIntToScalar(2)
76 , SW - SkIntToScalar(2), SH - SkIntToScalar(2)
77 ));
78
79 SkRandom rand;
80 for (int i = 0; i < N; i++) {
81 SkRect r;
82 rnd_rect(&r, &paint, rand);
83 canvas->drawOval(r, paint);
84 rnd_rect(&r, &paint, rand);
85 canvas->drawRoundRect(r, r.width()/4, r.height()/4, paint);
86 rnd_rect(&r, &paint, rand);
87 }
88 }
89 }
90
91 private:
92 typedef skiagm::GM INHERITED;
93 };
94
95 /* See
96 https://code.google.com/p/chromium/issues/detail?id=422974 and
97 http://jsfiddle.net/1xnku3sg/2/
98 */
99 class ZeroLenStrokesGM : public skiagm::GM {
100 SkPath fMoveHfPath, fMoveZfPath, fDashedfPath, fRefPath[4];
101 SkPath fCubicPath, fQuadPath, fLinePath;
102 protected:
onOnceBeforeDraw()103 void onOnceBeforeDraw() override {
104
105 SkAssertResult(SkParsePath::FromSVGString("M0,0h0M10,0h0M20,0h0", &fMoveHfPath));
106 SkAssertResult(SkParsePath::FromSVGString("M0,0zM10,0zM20,0z", &fMoveZfPath));
107 SkAssertResult(SkParsePath::FromSVGString("M0,0h25", &fDashedfPath));
108 SkAssertResult(SkParsePath::FromSVGString("M 0 0 C 0 0 0 0 0 0", &fCubicPath));
109 SkAssertResult(SkParsePath::FromSVGString("M 0 0 Q 0 0 0 0", &fQuadPath));
110 SkAssertResult(SkParsePath::FromSVGString("M 0 0 L 0 0", &fLinePath));
111
112 for (int i = 0; i < 3; ++i) {
113 fRefPath[0].addCircle(i * 10.f, 0, 5);
114 fRefPath[1].addCircle(i * 10.f, 0, 10);
115 fRefPath[2].addRect(i * 10.f - 4, -2, i * 10.f + 4, 6);
116 fRefPath[3].addRect(i * 10.f - 10, -10, i * 10.f + 10, 10);
117 }
118 }
119
onShortName()120 SkString onShortName() override {
121 return SkString("zeroPath");
122 }
123
onISize()124 SkISize onISize() override {
125 return SkISize::Make(W, H*2);
126 }
127
onDraw(SkCanvas * canvas)128 void onDraw(SkCanvas* canvas) override {
129 SkPaint fillPaint, strokePaint, dashPaint;
130 fillPaint.setAntiAlias(true);
131 strokePaint = fillPaint;
132 strokePaint.setStyle(SkPaint::kStroke_Style);
133 for (int i = 0; i < 2; ++i) {
134 fillPaint.setAlphaf(1.0f);
135 strokePaint.setAlphaf(1.0f);
136 strokePaint.setStrokeWidth(i ? 8.f : 10.f);
137 strokePaint.setStrokeCap(i ? SkPaint::kSquare_Cap : SkPaint::kRound_Cap);
138 canvas->save();
139 canvas->translate(10 + i * 100.f, 10);
140 canvas->drawPath(fMoveHfPath, strokePaint);
141 canvas->translate(0, 20);
142 canvas->drawPath(fMoveZfPath, strokePaint);
143 dashPaint = strokePaint;
144 const SkScalar intervals[] = { 0, 10 };
145 dashPaint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
146 SkPath fillPath;
147 dashPaint.getFillPath(fDashedfPath, &fillPath);
148 canvas->translate(0, 20);
149 canvas->drawPath(fDashedfPath, dashPaint);
150 canvas->translate(0, 20);
151 canvas->drawPath(fRefPath[i * 2], fillPaint);
152 strokePaint.setStrokeWidth(20);
153 strokePaint.setAlphaf(0.5f);
154 canvas->translate(0, 50);
155 canvas->drawPath(fMoveHfPath, strokePaint);
156 canvas->translate(0, 30);
157 canvas->drawPath(fMoveZfPath, strokePaint);
158 canvas->translate(0, 30);
159 fillPaint.setAlphaf(0.5f);
160 canvas->drawPath(fRefPath[1 + i * 2], fillPaint);
161 canvas->translate(0, 30);
162 canvas->drawPath(fCubicPath, strokePaint);
163 canvas->translate(0, 30);
164 canvas->drawPath(fQuadPath, strokePaint);
165 canvas->translate(0, 30);
166 canvas->drawPath(fLinePath, strokePaint);
167 canvas->restore();
168 }
169 }
170
171 private:
172 typedef skiagm::GM INHERITED;
173 };
174
175 class TeenyStrokesGM : public skiagm::GM {
176
onShortName()177 SkString onShortName() override {
178 return SkString("teenyStrokes");
179 }
180
onISize()181 SkISize onISize() override {
182 return SkISize::Make(W, H*2);
183 }
184
line(SkScalar scale,SkCanvas * canvas,SkColor color)185 static void line(SkScalar scale, SkCanvas* canvas, SkColor color) {
186 SkPaint p;
187 p.setAntiAlias(true);
188 p.setStyle(SkPaint::kStroke_Style);
189 p.setColor(color);
190 canvas->translate(50, 0);
191 canvas->save();
192 p.setStrokeWidth(scale * 5);
193 canvas->scale(1 / scale, 1 / scale);
194 canvas->drawLine(20 * scale, 20 * scale, 20 * scale, 100 * scale, p);
195 canvas->drawLine(20 * scale, 20 * scale, 100 * scale, 100 * scale, p);
196 canvas->restore();
197 }
198
onDraw(SkCanvas * canvas)199 void onDraw(SkCanvas* canvas) override {
200 line(0.00005f, canvas, SK_ColorBLACK);
201 line(0.000045f, canvas, SK_ColorRED);
202 line(0.0000035f, canvas, SK_ColorGREEN);
203 line(0.000003f, canvas, SK_ColorBLUE);
204 line(0.000002f, canvas, SK_ColorBLACK);
205 }
206 private:
207 typedef skiagm::GM INHERITED;
208 };
209
210 DEF_SIMPLE_GM(CubicStroke, canvas, 384, 384) {
211 SkPaint p;
212 p.setAntiAlias(true);
213 p.setStyle(SkPaint::kStroke_Style);
214 p.setStrokeWidth(1.0720f);
215 SkPath path;
216 path.moveTo(-6000,-6000);
217 path.cubicTo(-3500,5500,-500,5500,2500,-6500);
218 canvas->drawPath(path, p);
219 p.setStrokeWidth(1.0721f);
220 canvas->translate(10, 10);
221 canvas->drawPath(path, p);
222 p.setStrokeWidth(1.0722f);
223 canvas->translate(10, 10);
224 canvas->drawPath(path, p);
225 }
226
227 DEF_SIMPLE_GM(zerolinestroke, canvas, 90, 120) {
228 SkPaint paint;
229 paint.setStyle(SkPaint::kStroke_Style);
230 paint.setStrokeWidth(20);
231 paint.setAntiAlias(true);
232 paint.setStrokeCap(SkPaint::kRound_Cap);
233
234 SkPath path;
235 path.moveTo(30, 90);
236 path.lineTo(30, 90);
237 path.lineTo(60, 90);
238 path.lineTo(60, 90);
239 canvas->drawPath(path, paint);
240
241 path.reset();
242 path.moveTo(30, 30);
243 path.lineTo(60, 30);
244 canvas->drawPath(path, paint);
245
246 path.reset();
247 path.moveTo(30, 60);
248 path.lineTo(30, 60);
249 path.lineTo(60, 60);
250 canvas->drawPath(path, paint);
251 }
252
253 DEF_SIMPLE_GM(quadcap, canvas, 200, 200) {
254 SkPaint p;
255 p.setAntiAlias(true);
256 p.setStyle(SkPaint::kStroke_Style);
257 p.setStrokeWidth(0);
258 SkPath path;
259 SkPoint pts[] = {{105.738571f,13.126318f},
260 {105.738571f,13.126318f},
261 {123.753784f,1.f}};
262 SkVector tangent = pts[1] - pts[2];
263 tangent.normalize();
264 SkPoint pts2[3];
265 memcpy(pts2, pts, sizeof(pts));
266 const SkScalar capOutset = SK_ScalarPI / 8;
267 pts2[0].fX += tangent.fX * capOutset;
268 pts2[0].fY += tangent.fY * capOutset;
269 pts2[1].fX += tangent.fX * capOutset;
270 pts2[1].fY += tangent.fY * capOutset;
271 pts2[2].fX += -tangent.fX * capOutset;
272 pts2[2].fY += -tangent.fY * capOutset;
273 path.moveTo(pts2[0]);
274 path.quadTo(pts2[1], pts2[2]);
275 canvas->drawPath(path, p);
276
277 path.reset();
278 path.moveTo(pts[0]);
279 path.quadTo(pts[1], pts[2]);
280 p.setStrokeCap(SkPaint::kRound_Cap);
281 canvas->translate(30, 0);
282 canvas->drawPath(path, p);
283 }
284
285 class Strokes2GM : public skiagm::GM {
286 SkPath fPath;
287 protected:
onOnceBeforeDraw()288 void onOnceBeforeDraw() override {
289 SkRandom rand;
290 fPath.moveTo(0, 0);
291 for (int i = 0; i < 13; i++) {
292 SkScalar x = rand.nextUScalar1() * (W >> 1);
293 SkScalar y = rand.nextUScalar1() * (H >> 1);
294 fPath.lineTo(x, y);
295 }
296 }
297
298
onShortName()299 SkString onShortName() override {
300 return SkString("strokes_poly");
301 }
302
onISize()303 SkISize onISize() override {
304 return SkISize::Make(W, H*2);
305 }
306
onDraw(SkCanvas * canvas)307 void onDraw(SkCanvas* canvas) override {
308 canvas->drawColor(SK_ColorWHITE);
309
310 SkPaint paint;
311 paint.setStyle(SkPaint::kStroke_Style);
312 paint.setStrokeWidth(SkIntToScalar(9)/2);
313
314 for (int y = 0; y < 2; y++) {
315 paint.setAntiAlias(!!y);
316 SkAutoCanvasRestore acr(canvas, true);
317 canvas->translate(0, SH * y);
318 canvas->clipRect(SkRect::MakeLTRB(SkIntToScalar(2),
319 SkIntToScalar(2),
320 SW - SkIntToScalar(2),
321 SH - SkIntToScalar(2)));
322
323 SkRandom rand;
324 for (int i = 0; i < N/2; i++) {
325 SkRect r;
326 rnd_rect(&r, &paint, rand);
327 canvas->rotate(SkIntToScalar(15), SW/2, SH/2);
328 canvas->drawPath(fPath, paint);
329 }
330 }
331 }
332
333 private:
334 typedef skiagm::GM INHERITED;
335 };
336
337 //////////////////////////////////////////////////////////////////////////////
338
inset(const SkRect & r)339 static SkRect inset(const SkRect& r) {
340 SkRect rr(r);
341 rr.inset(r.width()/10, r.height()/10);
342 return rr;
343 }
344
345 class Strokes3GM : public skiagm::GM {
make0(SkPath * path,const SkRect & bounds,SkString * title)346 static void make0(SkPath* path, const SkRect& bounds, SkString* title) {
347 path->addRect(bounds, SkPath::kCW_Direction);
348 path->addRect(inset(bounds), SkPath::kCW_Direction);
349 title->set("CW CW");
350 }
351
make1(SkPath * path,const SkRect & bounds,SkString * title)352 static void make1(SkPath* path, const SkRect& bounds, SkString* title) {
353 path->addRect(bounds, SkPath::kCW_Direction);
354 path->addRect(inset(bounds), SkPath::kCCW_Direction);
355 title->set("CW CCW");
356 }
357
make2(SkPath * path,const SkRect & bounds,SkString * title)358 static void make2(SkPath* path, const SkRect& bounds, SkString* title) {
359 path->addOval(bounds, SkPath::kCW_Direction);
360 path->addOval(inset(bounds), SkPath::kCW_Direction);
361 title->set("CW CW");
362 }
363
make3(SkPath * path,const SkRect & bounds,SkString * title)364 static void make3(SkPath* path, const SkRect& bounds, SkString* title) {
365 path->addOval(bounds, SkPath::kCW_Direction);
366 path->addOval(inset(bounds), SkPath::kCCW_Direction);
367 title->set("CW CCW");
368 }
369
make4(SkPath * path,const SkRect & bounds,SkString * title)370 static void make4(SkPath* path, const SkRect& bounds, SkString* title) {
371 path->addRect(bounds, SkPath::kCW_Direction);
372 SkRect r = bounds;
373 r.inset(bounds.width() / 10, -bounds.height() / 10);
374 path->addOval(r, SkPath::kCW_Direction);
375 title->set("CW CW");
376 }
377
make5(SkPath * path,const SkRect & bounds,SkString * title)378 static void make5(SkPath* path, const SkRect& bounds, SkString* title) {
379 path->addRect(bounds, SkPath::kCW_Direction);
380 SkRect r = bounds;
381 r.inset(bounds.width() / 10, -bounds.height() / 10);
382 path->addOval(r, SkPath::kCCW_Direction);
383 title->set("CW CCW");
384 }
385
386 public:
Strokes3GM()387 Strokes3GM() {}
388
389 protected:
390
onShortName()391 SkString onShortName() override {
392 return SkString("strokes3");
393 }
394
onISize()395 SkISize onISize() override {
396 return SkISize::Make(1500, 1500);
397 }
398
onDraw(SkCanvas * canvas)399 void onDraw(SkCanvas* canvas) override {
400 SkPaint origPaint;
401 origPaint.setAntiAlias(true);
402 origPaint.setStyle(SkPaint::kStroke_Style);
403 SkPaint fillPaint(origPaint);
404 fillPaint.setColor(SK_ColorRED);
405 SkPaint strokePaint(origPaint);
406 strokePaint.setColor(ToolUtils::color_to_565(0xFF4444FF));
407
408 void (*procs[])(SkPath*, const SkRect&, SkString*) = {
409 make0, make1, make2, make3, make4, make5
410 };
411
412 canvas->translate(SkIntToScalar(20), SkIntToScalar(80));
413
414 SkRect bounds = SkRect::MakeWH(SkIntToScalar(50), SkIntToScalar(50));
415 SkScalar dx = bounds.width() * 4/3;
416 SkScalar dy = bounds.height() * 5;
417
418 for (size_t i = 0; i < SK_ARRAY_COUNT(procs); ++i) {
419 SkPath orig;
420 SkString str;
421 procs[i](&orig, bounds, &str);
422
423 canvas->save();
424 for (int j = 0; j < 13; ++j) {
425 strokePaint.setStrokeWidth(SK_Scalar1 * j * j);
426 canvas->drawPath(orig, strokePaint);
427 canvas->drawPath(orig, origPaint);
428 SkPath fill;
429 strokePaint.getFillPath(orig, &fill);
430 canvas->drawPath(fill, fillPaint);
431 canvas->translate(dx + strokePaint.getStrokeWidth(), 0);
432 }
433 canvas->restore();
434 canvas->translate(0, dy);
435 }
436 }
437
438 private:
439 typedef skiagm::GM INHERITED;
440 };
441
442 class Strokes4GM : public skiagm::GM {
443 public:
Strokes4GM()444 Strokes4GM() {}
445
446 protected:
447
onShortName()448 SkString onShortName() override {
449 return SkString("strokes_zoomed");
450 }
451
onISize()452 SkISize onISize() override {
453 return SkISize::Make(W, H*2);
454 }
455
onDraw(SkCanvas * canvas)456 void onDraw(SkCanvas* canvas) override {
457 SkPaint paint;
458 paint.setStyle(SkPaint::kStroke_Style);
459 paint.setStrokeWidth(0.055f);
460
461 canvas->scale(1000, 1000);
462 canvas->drawCircle(0, 2, 1.97f, paint);
463 }
464
465 private:
466 typedef skiagm::GM INHERITED;
467 };
468
469 // Test stroking for curves that produce degenerate tangents when t is 0 or 1 (see bug 4191)
470 class Strokes5GM : public skiagm::GM {
471 public:
Strokes5GM()472 Strokes5GM() {}
473
474 protected:
475
onShortName()476 SkString onShortName() override {
477 return SkString("zero_control_stroke");
478 }
479
onISize()480 SkISize onISize() override {
481 return SkISize::Make(W, H*2);
482 }
483
onDraw(SkCanvas * canvas)484 void onDraw(SkCanvas* canvas) override {
485 SkPaint p;
486 p.setColor(SK_ColorRED);
487 p.setAntiAlias(true);
488 p.setStyle(SkPaint::kStroke_Style);
489 p.setStrokeWidth(40);
490 p.setStrokeCap(SkPaint::kButt_Cap);
491
492 SkPath path;
493 path.moveTo(157.474f,111.753f);
494 path.cubicTo(128.5f,111.5f,35.5f,29.5f,35.5f,29.5f);
495 canvas->drawPath(path, p);
496 path.reset();
497 path.moveTo(250, 50);
498 path.quadTo(280, 80, 280, 80);
499 canvas->drawPath(path, p);
500 path.reset();
501 path.moveTo(150, 50);
502 path.conicTo(180, 80, 180, 80, 0.707f);
503 canvas->drawPath(path, p);
504
505 path.reset();
506 path.moveTo(157.474f,311.753f);
507 path.cubicTo(157.474f,311.753f,85.5f,229.5f,35.5f,229.5f);
508 canvas->drawPath(path, p);
509 path.reset();
510 path.moveTo(280, 250);
511 path.quadTo(280, 250, 310, 280);
512 canvas->drawPath(path, p);
513 path.reset();
514 path.moveTo(180, 250);
515 path.conicTo(180, 250, 210, 280, 0.707f);
516 canvas->drawPath(path, p);
517 }
518
519 private:
520 typedef skiagm::GM INHERITED;
521 };
522
523
524 //////////////////////////////////////////////////////////////////////////////
525
526 DEF_GM( return new StrokesGM; )
DEF_GM(return new Strokes2GM;)527 DEF_GM( return new Strokes2GM; )
528 DEF_GM( return new Strokes3GM; )
529 DEF_GM( return new Strokes4GM; )
530 DEF_GM( return new Strokes5GM; )
531
532 DEF_GM( return new ZeroLenStrokesGM; )
533 DEF_GM( return new TeenyStrokesGM; )
534
535 DEF_SIMPLE_GM(zerolinedash, canvas, 256, 256) {
536 canvas->clear(SK_ColorWHITE);
537
538 SkPaint paint;
539 paint.setColor(SkColorSetARGB(255, 0, 0, 0));
540 paint.setStrokeWidth(11);
541 paint.setStrokeCap(SkPaint::kRound_Cap);
542 paint.setStrokeJoin(SkPaint::kBevel_Join);
543
544 SkScalar dash_pattern[] = {1, 5};
545 paint.setPathEffect(SkDashPathEffect::Make(dash_pattern, 2, 0));
546
547 canvas->drawLine(100, 100, 100, 100, paint);
548 }
549
550 #ifdef PDF_IS_FIXED_SO_THIS_DOESNT_BREAK_IT
551 DEF_SIMPLE_GM(longrect_dash, canvas, 250, 250) {
552 canvas->clear(SK_ColorWHITE);
553
554 SkPaint paint;
555 paint.setColor(SkColorSetARGB(255, 0, 0, 0));
556 paint.setStrokeWidth(5);
557 paint.setStrokeCap(SkPaint::kRound_Cap);
558 paint.setStrokeJoin(SkPaint::kBevel_Join);
559 paint.setStyle(SkPaint::kStroke_Style);
560 SkScalar dash_pattern[] = {1, 5};
561 paint.setPathEffect(SkDashPathEffect::Make(dash_pattern, 2, 0));
562 // try all combinations of stretching bounds
563 for (auto left : { 20.f, -100001.f } ) {
564 for (auto top : { 20.f, -100001.f } ) {
565 for (auto right : { 40.f, 100001.f } ) {
566 for (auto bottom : { 40.f, 100001.f } ) {
567 canvas->save();
568 canvas->clipRect({10, 10, 50, 50});
569 canvas->drawRect({left, top, right, bottom}, paint);
570 canvas->restore();
571 canvas->translate(60, 0);
572 }
573 }
574 canvas->translate(-60 * 4, 60);
575 }
576 }
577 }
578 #endif
579