1/*
2 * Copyright 2017 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 "include/core/SkTypes.h"
9
10#if SK_SUPPORT_GPU
11
12#include "include/core/SkCanvas.h"
13#include "include/core/SkFont.h"
14#include "include/core/SkPaint.h"
15#include "include/core/SkPath.h"
16#include "samplecode/Sample.h"
17#include "src/core/SkGeometry.h"
18
19enum class VerbType {
20    kTriangles,
21    kQuadratics,
22    kCubics,
23    kConics
24};
25
26static const char* verb_type_name(VerbType verbType) {
27    switch (verbType) {
28        case VerbType::kTriangles: return "kTriangles";
29        case VerbType::kQuadratics: return "kQuadratics";
30        case VerbType::kCubics: return "kCubics";
31        case VerbType::kConics: return "kConics";
32    }
33    SkUNREACHABLE;
34};
35
36/**
37 * This sample visualizes simple strokes.
38 */
39class StrokeVerbView : public Sample {
40    void onOnceBeforeDraw() override { this->updatePath(); }
41    void onDrawContent(SkCanvas*) override;
42
43    Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override;
44    bool onClick(Sample::Click*) override;
45    bool onChar(SkUnichar) override;
46    SkString name() override { return SkString("StrokeVerb"); }
47
48    class Click;
49
50    void updateAndInval() { this->updatePath(); }
51
52    void updatePath();
53
54    VerbType fVerbType = VerbType::kCubics;
55
56    SkPoint fPoints[4] = {
57            {100.05f, 100.05f}, {400.75f, 100.05f}, {400.75f, 300.95f}, {100.05f, 300.95f}};
58
59    float fConicWeight = .5;
60    float fStrokeWidth = 40;
61    SkPaint::Join fStrokeJoin = SkPaint::kMiter_Join;
62    SkPaint::Cap fStrokeCap = SkPaint::kButt_Cap;
63
64    SkPath fPath;
65};
66
67void StrokeVerbView::onDrawContent(SkCanvas* canvas) {
68    canvas->clear(SK_ColorBLACK);
69
70    SkPaint outlinePaint;
71    outlinePaint.setColor(0xff808080);
72    outlinePaint.setStyle(SkPaint::kStroke_Style);
73    outlinePaint.setStrokeWidth(fStrokeWidth);
74    outlinePaint.setStrokeJoin(fStrokeJoin);
75    outlinePaint.setStrokeCap(fStrokeCap);
76    outlinePaint.setAntiAlias(true);
77    canvas->drawPath(fPath, outlinePaint);
78
79    SkString caption;
80    caption.appendf("VerbType_%s", verb_type_name(fVerbType));
81    if (VerbType::kCubics == fVerbType) {
82        caption.appendf(" (%s)", SkCubicTypeName(SkClassifyCubic(fPoints)));
83    } else if (VerbType::kConics == fVerbType) {
84        caption.appendf(" (w=%f)", fConicWeight);
85    }
86
87    caption.appendf(" (stroke_width=%f)", fStrokeWidth);
88
89    SkPaint pointsPaint;
90    pointsPaint.setColor(SK_ColorBLUE);
91    pointsPaint.setStrokeWidth(8);
92    pointsPaint.setAntiAlias(true);
93
94    if (VerbType::kCubics == fVerbType) {
95        canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPoints, pointsPaint);
96    } else {
97        canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, fPoints, pointsPaint);
98        canvas->drawPoints(SkCanvas::kPoints_PointMode, 1, fPoints + 3, pointsPaint);
99    }
100
101    SkFont font(nullptr, 20);
102    SkPaint captionPaint;
103    captionPaint.setColor(SK_ColorWHITE);
104    canvas->drawString(caption, 10, 30, font, captionPaint);
105}
106
107void StrokeVerbView::updatePath() {
108    fPath.reset();
109    fPath.moveTo(fPoints[0]);
110    switch (fVerbType) {
111        case VerbType::kCubics:
112            fPath.cubicTo(fPoints[1], fPoints[2], fPoints[3]);
113            break;
114        case VerbType::kQuadratics:
115            fPath.quadTo(fPoints[1], fPoints[3]);
116            break;
117        case VerbType::kConics:
118            fPath.conicTo(fPoints[1], fPoints[3], fConicWeight);
119            break;
120        case VerbType::kTriangles:
121            fPath.lineTo(fPoints[1]);
122            fPath.lineTo(fPoints[3]);
123            fPath.close();
124            break;
125    }
126}
127
128class StrokeVerbView::Click : public Sample::Click {
129public:
130    Click(int ptIdx) : fPtIdx(ptIdx) {}
131
132    void doClick(SkPoint points[]) {
133        if (fPtIdx >= 0) {
134            points[fPtIdx] += fCurr - fPrev;
135        } else {
136            for (int i = 0; i < 4; ++i) {
137                points[i] += fCurr - fPrev;
138            }
139        }
140    }
141
142private:
143    int fPtIdx;
144};
145
146Sample::Click* StrokeVerbView::onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) {
147    for (int i = 0; i < 4; ++i) {
148        if (VerbType::kCubics != fVerbType && 2 == i) {
149            continue;
150        }
151        if (fabs(x - fPoints[i].x()) < 20 && fabsf(y - fPoints[i].y()) < 20) {
152            return new Click(i);
153        }
154    }
155    return new Click(-1);
156}
157
158bool StrokeVerbView::onClick(Sample::Click* click) {
159    Click* myClick = (Click*)click;
160    myClick->doClick(fPoints);
161    this->updateAndInval();
162    return true;
163}
164
165bool StrokeVerbView::onChar(SkUnichar unichar) {
166        if (unichar >= '1' && unichar <= '4') {
167            fVerbType = VerbType(unichar - '1');
168            this->updateAndInval();
169            return true;
170        }
171        float* valueToScale = nullptr;
172        if (VerbType::kConics == fVerbType) {
173            valueToScale = &fConicWeight;
174        } else {
175            valueToScale = &fStrokeWidth;
176        }
177        if (valueToScale) {
178            if (unichar == '+') {
179                *valueToScale *= 2;
180                this->updateAndInval();
181                return true;
182            }
183            if (unichar == '+' || unichar == '=') {
184                *valueToScale *= 5/4.f;
185                this->updateAndInval();
186                return true;
187            }
188            if (unichar == '-') {
189                *valueToScale *= 4/5.f;
190                this->updateAndInval();
191                return true;
192            }
193            if (unichar == '_') {
194                *valueToScale *= .5f;
195                this->updateAndInval();
196                return true;
197            }
198        }
199        if (unichar == 'D') {
200            SkDebugf("    SkPoint fPoints[4] = {\n");
201            SkDebugf("        {%ff, %ff},\n", fPoints[0].x(), fPoints[0].y());
202            SkDebugf("        {%ff, %ff},\n", fPoints[1].x(), fPoints[1].y());
203            SkDebugf("        {%ff, %ff},\n", fPoints[2].x(), fPoints[2].y());
204            SkDebugf("        {%ff, %ff}\n", fPoints[3].x(), fPoints[3].y());
205            SkDebugf("    };\n");
206            return true;
207        }
208        if (unichar == 'J') {
209            fStrokeJoin = (SkPaint::Join)((fStrokeJoin + 1) % 3);
210            this->updateAndInval();
211            return true;
212        }
213        if (unichar == 'C') {
214            fStrokeCap = (SkPaint::Cap)((fStrokeCap + 1) % 3);
215            this->updateAndInval();
216            return true;
217        }
218        return false;
219}
220
221DEF_SAMPLE(return new StrokeVerbView;)
222
223#endif  // SK_SUPPORT_GPU
224