1/*
2 * Copyright 2016 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#include "include/core/SkCanvas.h"
8#include "include/core/SkPath.h"
9#include "samplecode/Sample.h"
10
11#include <iostream>
12#include <cmath>
13
14#define PI SK_ScalarPI
15
16#define LIN_SEGMENTS 10
17
18class OverstrokeView : public Sample {
19   public:
20    SkScalar fStroke;
21    int fPathType;  // super lazy enum
22    bool fClosePath;
23    bool fDrawFillPath;
24    bool fDumpHex;
25    OverstrokeView() {
26        fStroke = 5;
27        fPathType = 0;
28        fClosePath = false;
29        fDrawFillPath = false;
30        fDumpHex = false;
31        this->setBGColor(0xFFFFFFFF);
32    }
33
34   protected:
35    SkString name() override { return SkString("PathOverstroke"); }
36
37    bool onChar(SkUnichar uni) override {
38            switch (uni) {
39                case ',':
40                    fStroke += 1.0;
41                    return true;
42                case '.':
43                    fStroke -= 1.0;
44                    return true;
45                case 'x':
46                    fPathType = (fPathType + 1) % 4;
47                    return true;
48                case 'c':
49                    fClosePath = !fClosePath;
50                    return true;
51                case 'f':
52                    fDrawFillPath = !fDrawFillPath;
53                    return true;
54                case 'D':
55                    fDumpHex = !fDumpHex;
56                    return true;
57                default:
58                    break;
59            }
60            return false;
61    }
62
63    SkPath quadPath(SkPoint p1, SkPoint p2) {
64        SkASSERT(p1.y() == p2.y());
65
66        SkPath path;
67        path.moveTo(p1);
68        path.lineTo(p2);
69
70        SkPoint p3 = SkPoint::Make((p1.x() + p2.x()) / 2.0f, p1.y() * 0.7f);
71
72        path.quadTo(p3, p1);
73
74        return path;
75    }
76
77    SkPath cubicPath(SkPoint p1, SkPoint p2) {
78        SkASSERT(p1.y() == p2.y());
79
80        SkPath path;
81        path.moveTo(p1);
82
83        SkPoint p3 = SkPoint::Make((p1.x() + p2.x()) / 3.0f, p1.y() * 0.7f);
84        SkPoint p4 = SkPoint::Make(2.0f*(p1.x() + p2.x()) / 3.0f, p1.y() * 1.5f);
85
86        path.cubicTo(p3, p4, p2);
87
88        return path;
89    }
90
91    SkPath linSemicirclePath(SkPoint p1, SkPoint p2) {
92        SkASSERT(p1.y() == p2.y());
93
94        SkPath path;
95        path.moveTo(p1);
96        path.lineTo(p2);
97
98        SkPoint pt;
99
100        for (int i = 0; i < LIN_SEGMENTS; i++) {
101            float theta = i * PI / (LIN_SEGMENTS);
102            SkScalar x = 65 + 15 * cos(theta);
103            SkScalar y = 50 - 15 * sin(theta);
104            pt = SkPoint::Make(x, y);
105            path.lineTo(pt);
106        }
107        path.lineTo(p1);
108
109        return path;
110    }
111
112    SkPath rectPath(SkPoint p1) {
113        SkRect r = SkRect::MakeXYWH(p1.fX, p1.fY, 20, 20);
114        SkPath path;
115        path.addRect(r);
116
117        return path;
118    }
119
120    void onDrawContent(SkCanvas* canvas) override {
121        const float SCALE = 1;
122
123        canvas->translate(30, 40);
124        canvas->scale(SCALE, SCALE);
125
126        SkPoint p1 = SkPoint::Make(50, 50);
127        SkPoint p2 = SkPoint::Make(80, 50);
128
129        SkPath path;
130        switch (fPathType) {
131            case 0:
132                path = quadPath(p1, p2);
133                break;
134            case 1:
135                path = cubicPath(p1, p2);
136                break;
137            case 2:
138                path = rectPath(p1);
139                break;
140            case 3:
141                path = linSemicirclePath(p1, p2);
142                break;
143            default:
144                path = quadPath(p1, p2);
145                break;
146        }
147
148        if (fClosePath) {
149            path.close();
150        }
151
152        SkPaint p;
153        p.setColor(SK_ColorRED);
154        p.setAntiAlias(true);
155        p.setStyle(SkPaint::kStroke_Style);
156        p.setStrokeWidth(fStroke);
157
158        canvas->drawPath(path, p);
159
160        if (fDumpHex) {
161            std::cerr << "path dumpHex" << std::endl;
162            path.dumpHex();
163        }
164
165        SkPaint hairp;
166        hairp.setColor(SK_ColorBLACK);
167        hairp.setAntiAlias(true);
168        hairp.setStyle(SkPaint::kStroke_Style);
169
170        if (fDrawFillPath) {
171            SkPath fillpath;
172            p.getFillPath(path, &fillpath);
173
174            canvas->drawPath(fillpath, hairp);
175
176            if (fDumpHex) {
177                std::cerr << "fillpath dumpHex" << std::endl;
178                fillpath.dumpHex();
179            }
180        }
181
182        if (fDumpHex) {
183            std::cerr << std::endl;
184
185            fDumpHex = false;
186        }
187
188        // draw original path with green hairline
189        hairp.setColor(SK_ColorGREEN);
190        canvas->drawPath(path, hairp);
191    }
192
193   private:
194    using INHERITED = Sample;
195};
196
197///////////////////////////////////////////////////////////////////////////////
198
199DEF_SAMPLE( return new OverstrokeView(); )
200