1 /*
2 * Copyright 2018 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.h"
9 #include "SkCanvas.h"
10 #include "SkGeometry.h"
11 #include "SkPath.h"
12 #include "SkPaint.h"
13 #include "SkStrokeRec.h"
14
15 static constexpr float kStrokeWidth = 40;
16 static constexpr int kCellSize = 200;
17
18 static const SkPoint kCubics[][4] = {
19 {{122, 737}, {348, 553}, {403, 761}, {400, 760}},
20 {{244, 520}, {244, 518}, {1141, 634}, {394, 688}},
21 {{550, 194}, {138, 130}, {1035, 246}, {288, 300}},
22 {{226, 733}, {556, 779}, {-43, 471}, {348, 683}},
23 {{268, 204}, {492, 304}, {352, 23}, {433, 412}},
24 {{172, 480}, {396, 580}, {256, 299}, {338, 677}},
25 {{731, 340}, {318, 252}, {1026, -64}, {367, 265}},
26 {{475, 708}, {62, 620}, {770, 304}, {220, 659}},
27 };
28
calc_tight_cubic_bounds(const SkPoint P[4],int depth=5)29 static SkRect calc_tight_cubic_bounds(const SkPoint P[4], int depth=5) {
30 if (0 == depth) {
31 SkRect bounds;
32 bounds.fLeft = SkTMin(SkTMin(P[0].x(), P[1].x()), SkTMin(P[2].x(), P[3].x()));
33 bounds.fTop = SkTMin(SkTMin(P[0].y(), P[1].y()), SkTMin(P[2].y(), P[3].y()));
34 bounds.fRight = SkTMax(SkTMax(P[0].x(), P[1].x()), SkTMax(P[2].x(), P[3].x()));
35 bounds.fBottom = SkTMax(SkTMax(P[0].y(), P[1].y()), SkTMax(P[2].y(), P[3].y()));
36 return bounds;
37 }
38
39 SkPoint chopped[7];
40 SkChopCubicAt(P, chopped, .5f);
41 SkRect bounds = calc_tight_cubic_bounds(chopped, depth - 1);
42 bounds.join(calc_tight_cubic_bounds(chopped+3, depth - 1));
43 return bounds;
44 }
45
46 // This is a compilation of cubics that have given strokers grief. Feel free to add more.
47 class TrickyCubicStrokesGM : public skiagm::GM {
48 public:
TrickyCubicStrokesGM()49 TrickyCubicStrokesGM() {}
50
51 protected:
52
onShortName()53 SkString onShortName() override {
54 return SkString("trickycubicstrokes");
55 }
56
onISize()57 SkISize onISize() override {
58 return SkISize::Make(3*kCellSize, 3*kCellSize);
59 }
60
onOnceBeforeDraw()61 void onOnceBeforeDraw() override {
62 fStrokePaint.setAntiAlias(true);
63 fStrokePaint.setStrokeWidth(kStrokeWidth);
64 fStrokePaint.setColor(SK_ColorGREEN);
65 fStrokePaint.setStyle(SkPaint::kStroke_Style);
66 }
67
onDraw(SkCanvas * canvas)68 void onDraw(SkCanvas* canvas) override {
69 canvas->clear(SK_ColorBLACK);
70
71 for (size_t i = 0; i < SK_ARRAY_COUNT(kCubics); ++i) {
72 this->drawStroke(canvas, kCubics[i],
73 SkRect::MakeXYWH((i%3) * kCellSize, (i/3) * kCellSize, kCellSize,
74 kCellSize));
75 }
76 }
77
drawStroke(SkCanvas * canvas,const SkPoint P[4],const SkRect & location)78 void drawStroke(SkCanvas* canvas, const SkPoint P[4], const SkRect& location) {
79 SkRect strokeBounds = calc_tight_cubic_bounds(P);
80 strokeBounds.outset(kStrokeWidth, kStrokeWidth);
81
82 SkMatrix matrix;
83 matrix.setRectToRect(strokeBounds, location, SkMatrix::kCenter_ScaleToFit);
84
85 SkPath path;
86 path.moveTo(P[0]);
87 path.cubicTo(P[1], P[2], P[3]);
88
89 SkAutoCanvasRestore acr(canvas, true);
90 canvas->concat(matrix);
91 canvas->drawPath(path, fStrokePaint);
92 }
93
94 private:
95 SkPaint fStrokePaint;
96 typedef GM INHERITED;
97 };
98
99 DEF_GM( return new TrickyCubicStrokesGM; )
100