1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2012 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "include/core/SkBlendMode.h"
9cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h"
10cb93a386Sopenharmony_ci#include "include/core/SkColor.h"
11cb93a386Sopenharmony_ci#include "include/core/SkFont.h"
12cb93a386Sopenharmony_ci#include "include/core/SkImageInfo.h"
13cb93a386Sopenharmony_ci#include "include/core/SkMatrix.h"
14cb93a386Sopenharmony_ci#include "include/core/SkPaint.h"
15cb93a386Sopenharmony_ci#include "include/core/SkPath.h"
16cb93a386Sopenharmony_ci#include "include/core/SkPathMeasure.h"
17cb93a386Sopenharmony_ci#include "include/core/SkPoint.h"
18cb93a386Sopenharmony_ci#include "include/core/SkRRect.h"
19cb93a386Sopenharmony_ci#include "include/core/SkRect.h"
20cb93a386Sopenharmony_ci#include "include/core/SkRefCnt.h"
21cb93a386Sopenharmony_ci#include "include/core/SkScalar.h"
22cb93a386Sopenharmony_ci#include "include/core/SkShader.h"
23cb93a386Sopenharmony_ci#include "include/core/SkString.h"
24cb93a386Sopenharmony_ci#include "include/core/SkSurface.h"
25cb93a386Sopenharmony_ci#include "include/core/SkTypes.h"
26cb93a386Sopenharmony_ci#include "include/private/SkTArray.h"
27cb93a386Sopenharmony_ci#include "include/private/SkTemplates.h"
28cb93a386Sopenharmony_ci#include "include/utils/SkTextUtils.h"
29cb93a386Sopenharmony_ci#include "samplecode/Sample.h"
30cb93a386Sopenharmony_ci#include "src/core/SkGeometry.h"
31cb93a386Sopenharmony_ci#include "src/core/SkPathPriv.h"
32cb93a386Sopenharmony_ci#include "src/core/SkPointPriv.h"
33cb93a386Sopenharmony_ci#include "src/core/SkStroke.h"
34cb93a386Sopenharmony_ci#include "tools/ToolUtils.h"
35cb93a386Sopenharmony_ci
36cb93a386Sopenharmony_ci#include <cfloat>
37cb93a386Sopenharmony_ci
38cb93a386Sopenharmony_ciclass SkEvent;
39cb93a386Sopenharmony_ci
40cb93a386Sopenharmony_cistatic bool hittest(const SkPoint& target, SkScalar x, SkScalar y) {
41cb93a386Sopenharmony_ci    const SkScalar TOL = 7;
42cb93a386Sopenharmony_ci    return SkPoint::Distance(target, SkPoint::Make(x, y)) <= TOL;
43cb93a386Sopenharmony_ci}
44cb93a386Sopenharmony_ci
45cb93a386Sopenharmony_cistatic int getOnCurvePoints(const SkPath& path, SkPoint storage[]) {
46cb93a386Sopenharmony_ci    int count = 0;
47cb93a386Sopenharmony_ci    for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
48cb93a386Sopenharmony_ci        switch (verb) {
49cb93a386Sopenharmony_ci            case SkPathVerb::kMove:
50cb93a386Sopenharmony_ci            case SkPathVerb::kLine:
51cb93a386Sopenharmony_ci            case SkPathVerb::kQuad:
52cb93a386Sopenharmony_ci            case SkPathVerb::kConic:
53cb93a386Sopenharmony_ci            case SkPathVerb::kCubic:
54cb93a386Sopenharmony_ci                storage[count++] = pts[0];
55cb93a386Sopenharmony_ci                break;
56cb93a386Sopenharmony_ci            default:
57cb93a386Sopenharmony_ci                break;
58cb93a386Sopenharmony_ci        }
59cb93a386Sopenharmony_ci    }
60cb93a386Sopenharmony_ci    return count;
61cb93a386Sopenharmony_ci}
62cb93a386Sopenharmony_ci
63cb93a386Sopenharmony_cistatic void getContourCounts(const SkPath& path, SkTArray<int>* contourCounts) {
64cb93a386Sopenharmony_ci    int count = 0;
65cb93a386Sopenharmony_ci    for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
66cb93a386Sopenharmony_ci        switch (verb) {
67cb93a386Sopenharmony_ci            case SkPathVerb::kMove:
68cb93a386Sopenharmony_ci            case SkPathVerb::kLine:
69cb93a386Sopenharmony_ci                count += 1;
70cb93a386Sopenharmony_ci                break;
71cb93a386Sopenharmony_ci            case SkPathVerb::kQuad:
72cb93a386Sopenharmony_ci            case SkPathVerb::kConic:
73cb93a386Sopenharmony_ci                count += 2;
74cb93a386Sopenharmony_ci                break;
75cb93a386Sopenharmony_ci            case SkPathVerb::kCubic:
76cb93a386Sopenharmony_ci                count += 3;
77cb93a386Sopenharmony_ci                break;
78cb93a386Sopenharmony_ci            case SkPathVerb::kClose:
79cb93a386Sopenharmony_ci                contourCounts->push_back(count);
80cb93a386Sopenharmony_ci                count = 0;
81cb93a386Sopenharmony_ci                break;
82cb93a386Sopenharmony_ci            default:
83cb93a386Sopenharmony_ci                break;
84cb93a386Sopenharmony_ci        }
85cb93a386Sopenharmony_ci    }
86cb93a386Sopenharmony_ci    if (count > 0) {
87cb93a386Sopenharmony_ci        contourCounts->push_back(count);
88cb93a386Sopenharmony_ci    }
89cb93a386Sopenharmony_ci}
90cb93a386Sopenharmony_ci
91cb93a386Sopenharmony_cistatic void erase(const sk_sp<SkSurface>& surface) {
92cb93a386Sopenharmony_ci    SkCanvas* canvas = surface->getCanvas();
93cb93a386Sopenharmony_ci    if (canvas) {
94cb93a386Sopenharmony_ci        canvas->clear(SK_ColorTRANSPARENT);
95cb93a386Sopenharmony_ci    }
96cb93a386Sopenharmony_ci}
97cb93a386Sopenharmony_ci
98cb93a386Sopenharmony_cistruct StrokeTypeButton {
99cb93a386Sopenharmony_ci    SkRect fBounds;
100cb93a386Sopenharmony_ci    char fLabel;
101cb93a386Sopenharmony_ci    bool fEnabled;
102cb93a386Sopenharmony_ci};
103cb93a386Sopenharmony_ci
104cb93a386Sopenharmony_cistruct CircleTypeButton : public StrokeTypeButton {
105cb93a386Sopenharmony_ci    bool fFill;
106cb93a386Sopenharmony_ci};
107cb93a386Sopenharmony_ci
108cb93a386Sopenharmony_ciclass QuadStrokerView : public Sample {
109cb93a386Sopenharmony_ci    enum {
110cb93a386Sopenharmony_ci        SKELETON_COLOR = 0xFF0000FF,
111cb93a386Sopenharmony_ci        WIREFRAME_COLOR = 0x80FF0000
112cb93a386Sopenharmony_ci    };
113cb93a386Sopenharmony_ci
114cb93a386Sopenharmony_ci    enum {
115cb93a386Sopenharmony_ci        kCount = 18
116cb93a386Sopenharmony_ci    };
117cb93a386Sopenharmony_ci    SkPoint fPts[kCount];
118cb93a386Sopenharmony_ci    SkRect fWeightControl;
119cb93a386Sopenharmony_ci    SkRect fRadiusControl;
120cb93a386Sopenharmony_ci    SkRect fErrorControl;
121cb93a386Sopenharmony_ci    SkRect fWidthControl;
122cb93a386Sopenharmony_ci    SkRect fBounds;
123cb93a386Sopenharmony_ci    SkMatrix fMatrix, fInverse;
124cb93a386Sopenharmony_ci    sk_sp<SkShader> fShader;
125cb93a386Sopenharmony_ci    sk_sp<SkSurface> fMinSurface;
126cb93a386Sopenharmony_ci    sk_sp<SkSurface> fMaxSurface;
127cb93a386Sopenharmony_ci    StrokeTypeButton fCubicButton;
128cb93a386Sopenharmony_ci    StrokeTypeButton fConicButton;
129cb93a386Sopenharmony_ci    StrokeTypeButton fQuadButton;
130cb93a386Sopenharmony_ci    StrokeTypeButton fArcButton;
131cb93a386Sopenharmony_ci    StrokeTypeButton fRRectButton;
132cb93a386Sopenharmony_ci    CircleTypeButton fCircleButton;
133cb93a386Sopenharmony_ci    StrokeTypeButton fTextButton;
134cb93a386Sopenharmony_ci    SkString fText;
135cb93a386Sopenharmony_ci    SkScalar fTextSize;
136cb93a386Sopenharmony_ci    SkScalar fWeight;
137cb93a386Sopenharmony_ci    SkScalar fRadius;
138cb93a386Sopenharmony_ci    SkScalar fWidth, fDWidth;
139cb93a386Sopenharmony_ci    SkScalar fWidthScale;
140cb93a386Sopenharmony_ci    int fW, fH, fZoom;
141cb93a386Sopenharmony_ci    bool fAnimate;
142cb93a386Sopenharmony_ci    bool fDrawRibs;
143cb93a386Sopenharmony_ci    bool fDrawTangents;
144cb93a386Sopenharmony_ci    bool fDrawTDivs;
145cb93a386Sopenharmony_ci#ifdef SK_DEBUG
146cb93a386Sopenharmony_ci    #define kStrokerErrorMin 0.001f
147cb93a386Sopenharmony_ci    #define kStrokerErrorMax 5
148cb93a386Sopenharmony_ci#endif
149cb93a386Sopenharmony_ci    #define kWidthMin 1
150cb93a386Sopenharmony_ci    #define kWidthMax 100
151cb93a386Sopenharmony_cipublic:
152cb93a386Sopenharmony_ci    QuadStrokerView() {
153cb93a386Sopenharmony_ci        this->setBGColor(SK_ColorLTGRAY);
154cb93a386Sopenharmony_ci
155cb93a386Sopenharmony_ci        fPts[0].set(50, 200);  // cubic
156cb93a386Sopenharmony_ci        fPts[1].set(50, 100);
157cb93a386Sopenharmony_ci        fPts[2].set(150, 50);
158cb93a386Sopenharmony_ci        fPts[3].set(300, 50);
159cb93a386Sopenharmony_ci
160cb93a386Sopenharmony_ci        fPts[4].set(350, 200);  // conic
161cb93a386Sopenharmony_ci        fPts[5].set(350, 100);
162cb93a386Sopenharmony_ci        fPts[6].set(450, 50);
163cb93a386Sopenharmony_ci
164cb93a386Sopenharmony_ci        fPts[7].set(150, 300);  // quad
165cb93a386Sopenharmony_ci        fPts[8].set(150, 200);
166cb93a386Sopenharmony_ci        fPts[9].set(250, 150);
167cb93a386Sopenharmony_ci
168cb93a386Sopenharmony_ci        fPts[10].set(250, 200);  // arc
169cb93a386Sopenharmony_ci        fPts[11].set(250, 300);
170cb93a386Sopenharmony_ci        fPts[12].set(150, 350);
171cb93a386Sopenharmony_ci
172cb93a386Sopenharmony_ci        fPts[13].set(200, 200); // rrect
173cb93a386Sopenharmony_ci        fPts[14].set(400, 400);
174cb93a386Sopenharmony_ci
175cb93a386Sopenharmony_ci        fPts[15].set(250, 250);  // oval
176cb93a386Sopenharmony_ci        fPts[16].set(450, 450);
177cb93a386Sopenharmony_ci
178cb93a386Sopenharmony_ci        fText = "a";
179cb93a386Sopenharmony_ci        fTextSize = 12;
180cb93a386Sopenharmony_ci        fWidth = 50;
181cb93a386Sopenharmony_ci        fDWidth = 0.25f;
182cb93a386Sopenharmony_ci        fWeight = 1;
183cb93a386Sopenharmony_ci        fRadius = 150;
184cb93a386Sopenharmony_ci
185cb93a386Sopenharmony_ci        fCubicButton.fLabel = 'C';
186cb93a386Sopenharmony_ci        fCubicButton.fEnabled = false;
187cb93a386Sopenharmony_ci        fConicButton.fLabel = 'K';
188cb93a386Sopenharmony_ci        fConicButton.fEnabled = false;
189cb93a386Sopenharmony_ci        fQuadButton.fLabel = 'Q';
190cb93a386Sopenharmony_ci        fQuadButton.fEnabled = false;
191cb93a386Sopenharmony_ci        fArcButton.fLabel = 'A';
192cb93a386Sopenharmony_ci        fArcButton.fEnabled = true;
193cb93a386Sopenharmony_ci        fRRectButton.fLabel = 'R';
194cb93a386Sopenharmony_ci        fRRectButton.fEnabled = false;
195cb93a386Sopenharmony_ci        fCircleButton.fLabel = 'O';
196cb93a386Sopenharmony_ci        fCircleButton.fEnabled = true;
197cb93a386Sopenharmony_ci        fCircleButton.fFill = true;
198cb93a386Sopenharmony_ci        fTextButton.fLabel = 'T';
199cb93a386Sopenharmony_ci        fTextButton.fEnabled = false;
200cb93a386Sopenharmony_ci        fAnimate = false;
201cb93a386Sopenharmony_ci        setAsNeeded();
202cb93a386Sopenharmony_ci    }
203cb93a386Sopenharmony_ci
204cb93a386Sopenharmony_ciprotected:
205cb93a386Sopenharmony_ci    SkString name() override { return SkString("QuadStroker"); }
206cb93a386Sopenharmony_ci
207cb93a386Sopenharmony_ci    bool onChar(SkUnichar uni) override {
208cb93a386Sopenharmony_ci        if (fTextButton.fEnabled) {
209cb93a386Sopenharmony_ci            switch (uni) {
210cb93a386Sopenharmony_ci                case ' ':
211cb93a386Sopenharmony_ci                    fText = "";
212cb93a386Sopenharmony_ci                    break;
213cb93a386Sopenharmony_ci                case '-':
214cb93a386Sopenharmony_ci                    fTextSize = std::max(1.0f, fTextSize - 1);
215cb93a386Sopenharmony_ci                    break;
216cb93a386Sopenharmony_ci                case '+':
217cb93a386Sopenharmony_ci                case '=':
218cb93a386Sopenharmony_ci                    fTextSize += 1;
219cb93a386Sopenharmony_ci                    break;
220cb93a386Sopenharmony_ci                default:
221cb93a386Sopenharmony_ci                    fText.appendUnichar(uni);
222cb93a386Sopenharmony_ci            }
223cb93a386Sopenharmony_ci            return true;
224cb93a386Sopenharmony_ci        }
225cb93a386Sopenharmony_ci        return false;
226cb93a386Sopenharmony_ci    }
227cb93a386Sopenharmony_ci
228cb93a386Sopenharmony_ci    void onSizeChange() override {
229cb93a386Sopenharmony_ci        fRadiusControl.setXYWH(this->width() - 200, 30, 30, 400);
230cb93a386Sopenharmony_ci        fWeightControl.setXYWH(this->width() - 150, 30, 30, 400);
231cb93a386Sopenharmony_ci        fErrorControl.setXYWH(this->width() - 100, 30, 30, 400);
232cb93a386Sopenharmony_ci        fWidthControl.setXYWH(this->width() -  50, 30, 30, 400);
233cb93a386Sopenharmony_ci        int buttonOffset = 450;
234cb93a386Sopenharmony_ci        fCubicButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
235cb93a386Sopenharmony_ci        buttonOffset += 50;
236cb93a386Sopenharmony_ci        fConicButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
237cb93a386Sopenharmony_ci        buttonOffset += 50;
238cb93a386Sopenharmony_ci        fQuadButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
239cb93a386Sopenharmony_ci        buttonOffset += 50;
240cb93a386Sopenharmony_ci        fArcButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
241cb93a386Sopenharmony_ci        buttonOffset += 50;
242cb93a386Sopenharmony_ci        fRRectButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
243cb93a386Sopenharmony_ci        buttonOffset += 50;
244cb93a386Sopenharmony_ci        fCircleButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
245cb93a386Sopenharmony_ci        buttonOffset += 50;
246cb93a386Sopenharmony_ci        fTextButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
247cb93a386Sopenharmony_ci        this->INHERITED::onSizeChange();
248cb93a386Sopenharmony_ci    }
249cb93a386Sopenharmony_ci
250cb93a386Sopenharmony_ci     void copyMinToMax() {
251cb93a386Sopenharmony_ci        erase(fMaxSurface);
252cb93a386Sopenharmony_ci        SkCanvas* canvas = fMaxSurface->getCanvas();
253cb93a386Sopenharmony_ci        canvas->save();
254cb93a386Sopenharmony_ci        canvas->concat(fMatrix);
255cb93a386Sopenharmony_ci        fMinSurface->draw(canvas, 0, 0);
256cb93a386Sopenharmony_ci        canvas->restore();
257cb93a386Sopenharmony_ci
258cb93a386Sopenharmony_ci        SkPaint paint;
259cb93a386Sopenharmony_ci        paint.setBlendMode(SkBlendMode::kClear);
260cb93a386Sopenharmony_ci        for (int iy = 1; iy < fH; ++iy) {
261cb93a386Sopenharmony_ci            SkScalar y = SkIntToScalar(iy * fZoom);
262cb93a386Sopenharmony_ci            canvas->drawLine(0, y - SK_ScalarHalf, 999, y - SK_ScalarHalf, paint);
263cb93a386Sopenharmony_ci        }
264cb93a386Sopenharmony_ci        for (int ix = 1; ix < fW; ++ix) {
265cb93a386Sopenharmony_ci            SkScalar x = SkIntToScalar(ix * fZoom);
266cb93a386Sopenharmony_ci            canvas->drawLine(x - SK_ScalarHalf, 0, x - SK_ScalarHalf, 999, paint);
267cb93a386Sopenharmony_ci        }
268cb93a386Sopenharmony_ci    }
269cb93a386Sopenharmony_ci
270cb93a386Sopenharmony_ci   void setWHZ(int width, int height, int zoom) {
271cb93a386Sopenharmony_ci        fZoom = zoom;
272cb93a386Sopenharmony_ci        fBounds.setIWH(width * zoom, height * zoom);
273cb93a386Sopenharmony_ci        fMatrix.setScale(SkIntToScalar(zoom), SkIntToScalar(zoom));
274cb93a386Sopenharmony_ci        fInverse.setScale(SK_Scalar1 / zoom, SK_Scalar1 / zoom);
275cb93a386Sopenharmony_ci        fShader = ToolUtils::create_checkerboard_shader(0xFFCCCCCC, 0xFFFFFFFF, zoom);
276cb93a386Sopenharmony_ci
277cb93a386Sopenharmony_ci        SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
278cb93a386Sopenharmony_ci        fMinSurface = SkSurface::MakeRaster(info);
279cb93a386Sopenharmony_ci        info = info.makeWH(width * zoom, height * zoom);
280cb93a386Sopenharmony_ci        fMaxSurface = SkSurface::MakeRaster(info);
281cb93a386Sopenharmony_ci    }
282cb93a386Sopenharmony_ci
283cb93a386Sopenharmony_ci    void draw_points(SkCanvas* canvas, const SkPath& path, SkColor color,
284cb93a386Sopenharmony_ci                     bool show_lines) {
285cb93a386Sopenharmony_ci        SkPaint paint;
286cb93a386Sopenharmony_ci        paint.setColor(color);
287cb93a386Sopenharmony_ci        paint.setAlpha(0x80);
288cb93a386Sopenharmony_ci        paint.setAntiAlias(true);
289cb93a386Sopenharmony_ci        int n = path.countPoints();
290cb93a386Sopenharmony_ci        SkAutoSTArray<32, SkPoint> pts(n);
291cb93a386Sopenharmony_ci        if (show_lines && fDrawTangents) {
292cb93a386Sopenharmony_ci            SkTArray<int> contourCounts;
293cb93a386Sopenharmony_ci            getContourCounts(path, &contourCounts);
294cb93a386Sopenharmony_ci            SkPoint* ptPtr = pts.get();
295cb93a386Sopenharmony_ci            for (int i = 0; i < contourCounts.count(); ++i) {
296cb93a386Sopenharmony_ci                int count = contourCounts[i];
297cb93a386Sopenharmony_ci                path.getPoints(ptPtr, count);
298cb93a386Sopenharmony_ci                canvas->drawPoints(SkCanvas::kPolygon_PointMode, count, ptPtr, paint);
299cb93a386Sopenharmony_ci                ptPtr += count;
300cb93a386Sopenharmony_ci            }
301cb93a386Sopenharmony_ci        } else {
302cb93a386Sopenharmony_ci            n = getOnCurvePoints(path, pts.get());
303cb93a386Sopenharmony_ci        }
304cb93a386Sopenharmony_ci        paint.setStrokeWidth(5);
305cb93a386Sopenharmony_ci        canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts.get(), paint);
306cb93a386Sopenharmony_ci    }
307cb93a386Sopenharmony_ci
308cb93a386Sopenharmony_ci    void draw_ribs(SkCanvas* canvas, const SkPath& path, SkScalar width,
309cb93a386Sopenharmony_ci                   SkColor color) {
310cb93a386Sopenharmony_ci        const SkScalar radius = width / 2;
311cb93a386Sopenharmony_ci
312cb93a386Sopenharmony_ci        SkPathMeasure meas(path, false);
313cb93a386Sopenharmony_ci        SkScalar total = meas.getLength();
314cb93a386Sopenharmony_ci
315cb93a386Sopenharmony_ci        SkScalar delta = 8;
316cb93a386Sopenharmony_ci        SkPaint paint, labelP;
317cb93a386Sopenharmony_ci        paint.setColor(color);
318cb93a386Sopenharmony_ci        labelP.setColor(color & 0xff5f9f5f);
319cb93a386Sopenharmony_ci        SkFont font;
320cb93a386Sopenharmony_ci        SkPoint pos, tan;
321cb93a386Sopenharmony_ci        int index = 0;
322cb93a386Sopenharmony_ci        for (SkScalar dist = 0; dist <= total; dist += delta) {
323cb93a386Sopenharmony_ci            if (meas.getPosTan(dist, &pos, &tan)) {
324cb93a386Sopenharmony_ci                tan.scale(radius);
325cb93a386Sopenharmony_ci                SkPointPriv::RotateCCW(&tan);
326cb93a386Sopenharmony_ci                canvas->drawLine(pos.x() + tan.x(), pos.y() + tan.y(),
327cb93a386Sopenharmony_ci                                 pos.x() - tan.x(), pos.y() - tan.y(), paint);
328cb93a386Sopenharmony_ci                if (0 == index % 10) {
329cb93a386Sopenharmony_ci                    SkString label;
330cb93a386Sopenharmony_ci                    label.appendS32(index);
331cb93a386Sopenharmony_ci                    SkRect dot = SkRect::MakeXYWH(pos.x() - 2, pos.y() - 2, 4, 4);
332cb93a386Sopenharmony_ci                    canvas->drawRect(dot, labelP);
333cb93a386Sopenharmony_ci                    canvas->drawString(label,
334cb93a386Sopenharmony_ci                        pos.x() - tan.x() * 1.25f, pos.y() - tan.y() * 1.25f, font, labelP);
335cb93a386Sopenharmony_ci                }
336cb93a386Sopenharmony_ci            }
337cb93a386Sopenharmony_ci            ++index;
338cb93a386Sopenharmony_ci        }
339cb93a386Sopenharmony_ci    }
340cb93a386Sopenharmony_ci
341cb93a386Sopenharmony_ci    void draw_t_divs(SkCanvas* canvas, const SkPath& path, SkScalar width, SkColor color) {
342cb93a386Sopenharmony_ci        const SkScalar radius = width / 2;
343cb93a386Sopenharmony_ci        SkPaint paint;
344cb93a386Sopenharmony_ci        paint.setColor(color);
345cb93a386Sopenharmony_ci        SkPathMeasure meas(path, false);
346cb93a386Sopenharmony_ci        SkScalar total = meas.getLength();
347cb93a386Sopenharmony_ci        SkScalar delta = 8;
348cb93a386Sopenharmony_ci        int ribs = 0;
349cb93a386Sopenharmony_ci        for (SkScalar dist = 0; dist <= total; dist += delta) {
350cb93a386Sopenharmony_ci            ++ribs;
351cb93a386Sopenharmony_ci        }
352cb93a386Sopenharmony_ci        const uint8_t* verbs = SkPathPriv::VerbData(path);
353cb93a386Sopenharmony_ci        if (path.countVerbs() < 2 || SkPath::kMove_Verb != verbs[0]) {
354cb93a386Sopenharmony_ci            SkASSERT(0);
355cb93a386Sopenharmony_ci            return;
356cb93a386Sopenharmony_ci        }
357cb93a386Sopenharmony_ci        auto verb = static_cast<SkPath::Verb>(verbs[1]);
358cb93a386Sopenharmony_ci        SkASSERT(SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb);
359cb93a386Sopenharmony_ci        const SkPoint* pts = SkPathPriv::PointData(path);
360cb93a386Sopenharmony_ci        SkPoint pos, tan;
361cb93a386Sopenharmony_ci        for (int index = 0; index < ribs; ++index) {
362cb93a386Sopenharmony_ci            SkScalar t = (SkScalar) index / ribs;
363cb93a386Sopenharmony_ci            switch (verb) {
364cb93a386Sopenharmony_ci                case SkPath::kLine_Verb:
365cb93a386Sopenharmony_ci                    tan = pts[1] - pts[0];
366cb93a386Sopenharmony_ci                    pos = pts[0];
367cb93a386Sopenharmony_ci                    pos.fX += tan.fX * t;
368cb93a386Sopenharmony_ci                    pos.fY += tan.fY * t;
369cb93a386Sopenharmony_ci                    break;
370cb93a386Sopenharmony_ci                case SkPath::kQuad_Verb:
371cb93a386Sopenharmony_ci                    pos = SkEvalQuadAt(pts, t);
372cb93a386Sopenharmony_ci                    tan = SkEvalQuadTangentAt(pts, t);
373cb93a386Sopenharmony_ci                    break;
374cb93a386Sopenharmony_ci                case SkPath::kConic_Verb: {
375cb93a386Sopenharmony_ci                    SkConic conic(pts, SkPathPriv::ConicWeightData(path)[0]);
376cb93a386Sopenharmony_ci                    pos = conic.evalAt(t);
377cb93a386Sopenharmony_ci                    tan = conic.evalTangentAt(t);
378cb93a386Sopenharmony_ci                    } break;
379cb93a386Sopenharmony_ci                case SkPath::kCubic_Verb:
380cb93a386Sopenharmony_ci                    SkEvalCubicAt(pts, t, &pos, &tan, nullptr);
381cb93a386Sopenharmony_ci                    break;
382cb93a386Sopenharmony_ci                default:
383cb93a386Sopenharmony_ci                    SkASSERT(0);
384cb93a386Sopenharmony_ci                    return;
385cb93a386Sopenharmony_ci            }
386cb93a386Sopenharmony_ci            tan.setLength(radius);
387cb93a386Sopenharmony_ci            SkPointPriv::RotateCCW(&tan);
388cb93a386Sopenharmony_ci            canvas->drawLine(pos.x() + tan.x(), pos.y() + tan.y(),
389cb93a386Sopenharmony_ci                                pos.x() - tan.x(), pos.y() - tan.y(), paint);
390cb93a386Sopenharmony_ci            if (0 == index % 10) {
391cb93a386Sopenharmony_ci                SkString label;
392cb93a386Sopenharmony_ci                label.appendS32(index);
393cb93a386Sopenharmony_ci                canvas->drawString(label,
394cb93a386Sopenharmony_ci                    pos.x() + tan.x() * 1.25f, pos.y() + tan.y() * 1.25f, SkFont(), paint);
395cb93a386Sopenharmony_ci            }
396cb93a386Sopenharmony_ci        }
397cb93a386Sopenharmony_ci    }
398cb93a386Sopenharmony_ci
399cb93a386Sopenharmony_ci    void draw_stroke(SkCanvas* canvas, const SkPath& path, SkScalar width, SkScalar scale,
400cb93a386Sopenharmony_ci            bool drawText) {
401cb93a386Sopenharmony_ci        if (path.isEmpty()) {
402cb93a386Sopenharmony_ci            return;
403cb93a386Sopenharmony_ci        }
404cb93a386Sopenharmony_ci        SkRect bounds = path.getBounds();
405cb93a386Sopenharmony_ci        this->setWHZ(SkScalarCeilToInt(bounds.right()), drawText
406cb93a386Sopenharmony_ci                ? SkScalarRoundToInt(scale * 3 / 2) : SkScalarRoundToInt(scale),
407cb93a386Sopenharmony_ci                SkScalarRoundToInt(950.0f / scale));
408cb93a386Sopenharmony_ci        erase(fMinSurface);
409cb93a386Sopenharmony_ci        SkPaint paint;
410cb93a386Sopenharmony_ci        paint.setColor(0x1f1f0f0f);
411cb93a386Sopenharmony_ci        paint.setStyle(SkPaint::kStroke_Style);
412cb93a386Sopenharmony_ci        paint.setStrokeWidth(width * scale * scale);
413cb93a386Sopenharmony_ci        paint.setColor(0x3f0f1f3f);
414cb93a386Sopenharmony_ci        if (drawText) {
415cb93a386Sopenharmony_ci            fMinSurface->getCanvas()->drawPath(path, paint);
416cb93a386Sopenharmony_ci            this->copyMinToMax();
417cb93a386Sopenharmony_ci            fMaxSurface->draw(canvas, 0, 0);
418cb93a386Sopenharmony_ci        }
419cb93a386Sopenharmony_ci        paint.setAntiAlias(true);
420cb93a386Sopenharmony_ci        paint.setStyle(SkPaint::kStroke_Style);
421cb93a386Sopenharmony_ci        paint.setStrokeWidth(1);
422cb93a386Sopenharmony_ci
423cb93a386Sopenharmony_ci        paint.setColor(SKELETON_COLOR);
424cb93a386Sopenharmony_ci        SkPath scaled;
425cb93a386Sopenharmony_ci        SkMatrix matrix;
426cb93a386Sopenharmony_ci        matrix.reset();
427cb93a386Sopenharmony_ci        matrix.setScale(950 / scale, 950 / scale);
428cb93a386Sopenharmony_ci        if (drawText) {
429cb93a386Sopenharmony_ci            path.transform(matrix, &scaled);
430cb93a386Sopenharmony_ci        } else {
431cb93a386Sopenharmony_ci            scaled = path;
432cb93a386Sopenharmony_ci        }
433cb93a386Sopenharmony_ci        canvas->drawPath(scaled, paint);
434cb93a386Sopenharmony_ci        draw_points(canvas, scaled, SKELETON_COLOR, true);
435cb93a386Sopenharmony_ci
436cb93a386Sopenharmony_ci        if (fDrawRibs) {
437cb93a386Sopenharmony_ci            draw_ribs(canvas, scaled, width, 0xFF00FF00);
438cb93a386Sopenharmony_ci        }
439cb93a386Sopenharmony_ci
440cb93a386Sopenharmony_ci        if (fDrawTDivs) {
441cb93a386Sopenharmony_ci            draw_t_divs(canvas, scaled, width, 0xFF3F3F00);
442cb93a386Sopenharmony_ci        }
443cb93a386Sopenharmony_ci
444cb93a386Sopenharmony_ci        SkPath fill;
445cb93a386Sopenharmony_ci
446cb93a386Sopenharmony_ci        SkPaint p;
447cb93a386Sopenharmony_ci        p.setStyle(SkPaint::kStroke_Style);
448cb93a386Sopenharmony_ci        if (drawText) {
449cb93a386Sopenharmony_ci            p.setStrokeWidth(width * scale * scale);
450cb93a386Sopenharmony_ci        } else {
451cb93a386Sopenharmony_ci            p.setStrokeWidth(width);
452cb93a386Sopenharmony_ci        }
453cb93a386Sopenharmony_ci        p.getFillPath(path, &fill);
454cb93a386Sopenharmony_ci        SkPath scaledFill;
455cb93a386Sopenharmony_ci        if (drawText) {
456cb93a386Sopenharmony_ci            fill.transform(matrix, &scaledFill);
457cb93a386Sopenharmony_ci        } else {
458cb93a386Sopenharmony_ci            scaledFill = fill;
459cb93a386Sopenharmony_ci        }
460cb93a386Sopenharmony_ci        paint.setColor(WIREFRAME_COLOR);
461cb93a386Sopenharmony_ci        canvas->drawPath(scaledFill, paint);
462cb93a386Sopenharmony_ci        draw_points(canvas, scaledFill, WIREFRAME_COLOR, false);
463cb93a386Sopenharmony_ci    }
464cb93a386Sopenharmony_ci
465cb93a386Sopenharmony_ci    void draw_fill(SkCanvas* canvas, const SkRect& rect, SkScalar width) {
466cb93a386Sopenharmony_ci        if (rect.isEmpty()) {
467cb93a386Sopenharmony_ci            return;
468cb93a386Sopenharmony_ci        }
469cb93a386Sopenharmony_ci        SkPaint paint;
470cb93a386Sopenharmony_ci        paint.setColor(0x1f1f0f0f);
471cb93a386Sopenharmony_ci        paint.setStyle(SkPaint::kStroke_Style);
472cb93a386Sopenharmony_ci        paint.setStrokeWidth(width);
473cb93a386Sopenharmony_ci        SkPath path;
474cb93a386Sopenharmony_ci        SkScalar maxSide = std::max(rect.width(), rect.height()) / 2;
475cb93a386Sopenharmony_ci        SkPoint center = { rect.fLeft + maxSide, rect.fTop + maxSide };
476cb93a386Sopenharmony_ci        path.addCircle(center.fX, center.fY, maxSide);
477cb93a386Sopenharmony_ci        canvas->drawPath(path, paint);
478cb93a386Sopenharmony_ci        paint.setStyle(SkPaint::kFill_Style);
479cb93a386Sopenharmony_ci        path.reset();
480cb93a386Sopenharmony_ci        path.addCircle(center.fX, center.fY, maxSide - width / 2);
481cb93a386Sopenharmony_ci        paint.setColor(0x3f0f1f3f);
482cb93a386Sopenharmony_ci        canvas->drawPath(path, paint);
483cb93a386Sopenharmony_ci        path.reset();
484cb93a386Sopenharmony_ci        path.setFillType(SkPathFillType::kEvenOdd);
485cb93a386Sopenharmony_ci        path.addCircle(center.fX, center.fY, maxSide + width / 2);
486cb93a386Sopenharmony_ci        SkRect outside = SkRect::MakeXYWH(center.fX - maxSide - width, center.fY - maxSide - width,
487cb93a386Sopenharmony_ci                (maxSide + width) * 2, (maxSide + width) * 2);
488cb93a386Sopenharmony_ci        path.addRect(outside);
489cb93a386Sopenharmony_ci        canvas->drawPath(path, paint);
490cb93a386Sopenharmony_ci    }
491cb93a386Sopenharmony_ci
492cb93a386Sopenharmony_ci    void draw_button(SkCanvas* canvas, const StrokeTypeButton& button) {
493cb93a386Sopenharmony_ci        SkPaint paint;
494cb93a386Sopenharmony_ci        paint.setAntiAlias(true);
495cb93a386Sopenharmony_ci        paint.setStyle(SkPaint::kStroke_Style);
496cb93a386Sopenharmony_ci        paint.setColor(button.fEnabled ? 0xFF3F0000 : 0x6F3F0000);
497cb93a386Sopenharmony_ci        canvas->drawRect(button.fBounds, paint);
498cb93a386Sopenharmony_ci        paint.setColor(button.fEnabled ? 0xFF3F0000 : 0x6F3F0000);
499cb93a386Sopenharmony_ci        paint.setStyle(SkPaint::kFill_Style);
500cb93a386Sopenharmony_ci        SkFont font;
501cb93a386Sopenharmony_ci        font.setSize(25.0f);
502cb93a386Sopenharmony_ci        SkTextUtils::Draw(canvas, &button.fLabel, 1, SkTextEncoding::kUTF8,
503cb93a386Sopenharmony_ci                button.fBounds.centerX(), button.fBounds.fBottom - 5,
504cb93a386Sopenharmony_ci                font, paint, SkTextUtils::kCenter_Align);
505cb93a386Sopenharmony_ci    }
506cb93a386Sopenharmony_ci
507cb93a386Sopenharmony_ci    void draw_control(SkCanvas* canvas, const SkRect& bounds, SkScalar value,
508cb93a386Sopenharmony_ci            SkScalar min, SkScalar max, const char* name) {
509cb93a386Sopenharmony_ci        SkPaint paint;
510cb93a386Sopenharmony_ci        paint.setAntiAlias(true);
511cb93a386Sopenharmony_ci        paint.setStyle(SkPaint::kStroke_Style);
512cb93a386Sopenharmony_ci        canvas->drawRect(bounds, paint);
513cb93a386Sopenharmony_ci        SkScalar scale = max - min;
514cb93a386Sopenharmony_ci        SkScalar yPos = bounds.fTop + (value - min) * bounds.height() / scale;
515cb93a386Sopenharmony_ci        paint.setColor(0xFFFF0000);
516cb93a386Sopenharmony_ci        canvas->drawLine(bounds.fLeft - 5, yPos, bounds.fRight + 5, yPos, paint);
517cb93a386Sopenharmony_ci        SkString label;
518cb93a386Sopenharmony_ci        label.printf("%0.3g", value);
519cb93a386Sopenharmony_ci        paint.setColor(0xFF000000);
520cb93a386Sopenharmony_ci        paint.setStyle(SkPaint::kFill_Style);
521cb93a386Sopenharmony_ci        SkFont font(nullptr, 11.0f);
522cb93a386Sopenharmony_ci        canvas->drawString(label, bounds.fLeft + 5, yPos - 5, font, paint);
523cb93a386Sopenharmony_ci        font.setSize(13.0f);
524cb93a386Sopenharmony_ci        canvas->drawString(name, bounds.fLeft, bounds.bottom() + 11, font, paint);
525cb93a386Sopenharmony_ci    }
526cb93a386Sopenharmony_ci
527cb93a386Sopenharmony_ci    void setForGeometry() {
528cb93a386Sopenharmony_ci        fDrawRibs = true;
529cb93a386Sopenharmony_ci        fDrawTangents = true;
530cb93a386Sopenharmony_ci        fDrawTDivs = false;
531cb93a386Sopenharmony_ci        fWidthScale = 1;
532cb93a386Sopenharmony_ci    }
533cb93a386Sopenharmony_ci
534cb93a386Sopenharmony_ci    void setForText() {
535cb93a386Sopenharmony_ci        fDrawRibs = fDrawTangents = fDrawTDivs = false;
536cb93a386Sopenharmony_ci        fWidthScale = 0.002f;
537cb93a386Sopenharmony_ci    }
538cb93a386Sopenharmony_ci
539cb93a386Sopenharmony_ci    void setForSingles() {
540cb93a386Sopenharmony_ci        setForGeometry();
541cb93a386Sopenharmony_ci        fDrawTDivs = true;
542cb93a386Sopenharmony_ci    }
543cb93a386Sopenharmony_ci
544cb93a386Sopenharmony_ci    void setAsNeeded() {
545cb93a386Sopenharmony_ci        if (fConicButton.fEnabled || fCubicButton.fEnabled || fQuadButton.fEnabled) {
546cb93a386Sopenharmony_ci            setForSingles();
547cb93a386Sopenharmony_ci        } else if (fRRectButton.fEnabled || fCircleButton.fEnabled || fArcButton.fEnabled) {
548cb93a386Sopenharmony_ci            setForGeometry();
549cb93a386Sopenharmony_ci        } else {
550cb93a386Sopenharmony_ci            setForText();
551cb93a386Sopenharmony_ci        }
552cb93a386Sopenharmony_ci    }
553cb93a386Sopenharmony_ci
554cb93a386Sopenharmony_ci    bool arcCenter(SkPoint* center) {
555cb93a386Sopenharmony_ci        SkPath path;
556cb93a386Sopenharmony_ci        path.moveTo(fPts[10]);
557cb93a386Sopenharmony_ci        path.arcTo(fPts[11], fPts[12], fRadius);
558cb93a386Sopenharmony_ci        SkPath::Iter iter(path, false);
559cb93a386Sopenharmony_ci        SkPoint pts[4];
560cb93a386Sopenharmony_ci        iter.next(pts);
561cb93a386Sopenharmony_ci        if (SkPath::kLine_Verb == iter.next(pts)) {
562cb93a386Sopenharmony_ci            iter.next(pts);
563cb93a386Sopenharmony_ci        }
564cb93a386Sopenharmony_ci        SkVector before = pts[0] - pts[1];
565cb93a386Sopenharmony_ci        SkVector after = pts[1] - pts[2];
566cb93a386Sopenharmony_ci        before.setLength(fRadius);
567cb93a386Sopenharmony_ci        after.setLength(fRadius);
568cb93a386Sopenharmony_ci        SkVector beforeCCW, afterCCW;
569cb93a386Sopenharmony_ci        SkPointPriv::RotateCCW(before, &beforeCCW);
570cb93a386Sopenharmony_ci        SkPointPriv::RotateCCW(after, &afterCCW);
571cb93a386Sopenharmony_ci        beforeCCW += pts[0];
572cb93a386Sopenharmony_ci        afterCCW += pts[2];
573cb93a386Sopenharmony_ci        *center = beforeCCW;
574cb93a386Sopenharmony_ci        if (SkScalarNearlyEqual(beforeCCW.fX, afterCCW.fX)
575cb93a386Sopenharmony_ci                && SkScalarNearlyEqual(beforeCCW.fY, afterCCW.fY)) {
576cb93a386Sopenharmony_ci            return true;
577cb93a386Sopenharmony_ci        }
578cb93a386Sopenharmony_ci        SkVector beforeCW, afterCW;
579cb93a386Sopenharmony_ci        SkPointPriv::RotateCW(before, &beforeCW);
580cb93a386Sopenharmony_ci        SkPointPriv::RotateCW(after, &afterCW);
581cb93a386Sopenharmony_ci        beforeCW += pts[0];
582cb93a386Sopenharmony_ci        afterCW += pts[2];
583cb93a386Sopenharmony_ci        *center = beforeCW;
584cb93a386Sopenharmony_ci        return SkScalarNearlyEqual(beforeCW.fX, afterCW.fX)
585cb93a386Sopenharmony_ci                && SkScalarNearlyEqual(beforeCCW.fY, afterCW.fY);
586cb93a386Sopenharmony_ci    }
587cb93a386Sopenharmony_ci
588cb93a386Sopenharmony_ci    void onDrawContent(SkCanvas* canvas) override {
589cb93a386Sopenharmony_ci        SkPath path;
590cb93a386Sopenharmony_ci        SkScalar width = fWidth;
591cb93a386Sopenharmony_ci
592cb93a386Sopenharmony_ci        if (fCubicButton.fEnabled) {
593cb93a386Sopenharmony_ci            path.moveTo(fPts[0]);
594cb93a386Sopenharmony_ci            path.cubicTo(fPts[1], fPts[2], fPts[3]);
595cb93a386Sopenharmony_ci            setForSingles();
596cb93a386Sopenharmony_ci            draw_stroke(canvas, path, width, 950, false);
597cb93a386Sopenharmony_ci        }
598cb93a386Sopenharmony_ci
599cb93a386Sopenharmony_ci        if (fConicButton.fEnabled) {
600cb93a386Sopenharmony_ci            path.reset();
601cb93a386Sopenharmony_ci            path.moveTo(fPts[4]);
602cb93a386Sopenharmony_ci            path.conicTo(fPts[5], fPts[6], fWeight);
603cb93a386Sopenharmony_ci            setForSingles();
604cb93a386Sopenharmony_ci            draw_stroke(canvas, path, width, 950, false);
605cb93a386Sopenharmony_ci        }
606cb93a386Sopenharmony_ci
607cb93a386Sopenharmony_ci        if (fQuadButton.fEnabled) {
608cb93a386Sopenharmony_ci            path.reset();
609cb93a386Sopenharmony_ci            path.moveTo(fPts[7]);
610cb93a386Sopenharmony_ci            path.quadTo(fPts[8], fPts[9]);
611cb93a386Sopenharmony_ci            setForSingles();
612cb93a386Sopenharmony_ci            draw_stroke(canvas, path, width, 950, false);
613cb93a386Sopenharmony_ci        }
614cb93a386Sopenharmony_ci
615cb93a386Sopenharmony_ci        if (fArcButton.fEnabled) {
616cb93a386Sopenharmony_ci            path.reset();
617cb93a386Sopenharmony_ci            path.moveTo(fPts[10]);
618cb93a386Sopenharmony_ci            path.arcTo(fPts[11], fPts[12], fRadius);
619cb93a386Sopenharmony_ci            setForGeometry();
620cb93a386Sopenharmony_ci            draw_stroke(canvas, path, width, 950, false);
621cb93a386Sopenharmony_ci            SkPath pathPts;
622cb93a386Sopenharmony_ci            pathPts.moveTo(fPts[10]);
623cb93a386Sopenharmony_ci            pathPts.lineTo(fPts[11]);
624cb93a386Sopenharmony_ci            pathPts.lineTo(fPts[12]);
625cb93a386Sopenharmony_ci            draw_points(canvas, pathPts, SK_ColorDKGRAY, true);
626cb93a386Sopenharmony_ci        }
627cb93a386Sopenharmony_ci
628cb93a386Sopenharmony_ci        if (fRRectButton.fEnabled) {
629cb93a386Sopenharmony_ci            SkScalar rad = 32;
630cb93a386Sopenharmony_ci            SkRect r;
631cb93a386Sopenharmony_ci            r.setBounds(&fPts[13], 2);
632cb93a386Sopenharmony_ci            path.reset();
633cb93a386Sopenharmony_ci            SkRRect rr;
634cb93a386Sopenharmony_ci            rr.setRectXY(r, rad, rad);
635cb93a386Sopenharmony_ci            path.addRRect(rr);
636cb93a386Sopenharmony_ci            setForGeometry();
637cb93a386Sopenharmony_ci            draw_stroke(canvas, path, width, 950, false);
638cb93a386Sopenharmony_ci
639cb93a386Sopenharmony_ci            path.reset();
640cb93a386Sopenharmony_ci            SkRRect rr2;
641cb93a386Sopenharmony_ci            rr.inset(width/2, width/2, &rr2);
642cb93a386Sopenharmony_ci            path.addRRect(rr2, SkPathDirection::kCCW);
643cb93a386Sopenharmony_ci            rr.inset(-width/2, -width/2, &rr2);
644cb93a386Sopenharmony_ci            path.addRRect(rr2, SkPathDirection::kCW);
645cb93a386Sopenharmony_ci            SkPaint paint;
646cb93a386Sopenharmony_ci            paint.setAntiAlias(true);
647cb93a386Sopenharmony_ci            paint.setColor(0x40FF8844);
648cb93a386Sopenharmony_ci            canvas->drawPath(path, paint);
649cb93a386Sopenharmony_ci        }
650cb93a386Sopenharmony_ci
651cb93a386Sopenharmony_ci        if (fCircleButton.fEnabled) {
652cb93a386Sopenharmony_ci            path.reset();
653cb93a386Sopenharmony_ci            SkRect r;
654cb93a386Sopenharmony_ci            r.setBounds(&fPts[15], 2);
655cb93a386Sopenharmony_ci            path.addOval(r);
656cb93a386Sopenharmony_ci            setForGeometry();
657cb93a386Sopenharmony_ci            if (fCircleButton.fFill) {
658cb93a386Sopenharmony_ci                if (fArcButton.fEnabled) {
659cb93a386Sopenharmony_ci                    SkPoint center;
660cb93a386Sopenharmony_ci                    if (arcCenter(&center)) {
661cb93a386Sopenharmony_ci                        r.setLTRB(center.fX - fRadius, center.fY - fRadius,
662cb93a386Sopenharmony_ci                                  center.fX + fRadius, center.fY + fRadius);
663cb93a386Sopenharmony_ci                    }
664cb93a386Sopenharmony_ci                }
665cb93a386Sopenharmony_ci                draw_fill(canvas, r, width);
666cb93a386Sopenharmony_ci            } else {
667cb93a386Sopenharmony_ci                draw_stroke(canvas, path, width, 950, false);
668cb93a386Sopenharmony_ci            }
669cb93a386Sopenharmony_ci        }
670cb93a386Sopenharmony_ci
671cb93a386Sopenharmony_ci        if (fTextButton.fEnabled) {
672cb93a386Sopenharmony_ci            path.reset();
673cb93a386Sopenharmony_ci            SkFont font;
674cb93a386Sopenharmony_ci            font.setSize(fTextSize);
675cb93a386Sopenharmony_ci            SkTextUtils::GetPath(fText.c_str(), fText.size(), SkTextEncoding::kUTF8,
676cb93a386Sopenharmony_ci                                 0, fTextSize, font, &path);
677cb93a386Sopenharmony_ci            setForText();
678cb93a386Sopenharmony_ci            draw_stroke(canvas, path, width * fWidthScale / fTextSize, fTextSize, true);
679cb93a386Sopenharmony_ci        }
680cb93a386Sopenharmony_ci
681cb93a386Sopenharmony_ci        if (fAnimate) {
682cb93a386Sopenharmony_ci            fWidth += fDWidth;
683cb93a386Sopenharmony_ci            if (fDWidth > 0 && fWidth > kWidthMax) {
684cb93a386Sopenharmony_ci                fDWidth = -fDWidth;
685cb93a386Sopenharmony_ci            } else if (fDWidth < 0 && fWidth < kWidthMin) {
686cb93a386Sopenharmony_ci                fDWidth = -fDWidth;
687cb93a386Sopenharmony_ci            }
688cb93a386Sopenharmony_ci        }
689cb93a386Sopenharmony_ci        setAsNeeded();
690cb93a386Sopenharmony_ci        if (fConicButton.fEnabled) {
691cb93a386Sopenharmony_ci            draw_control(canvas, fWeightControl, fWeight, 0, 5, "weight");
692cb93a386Sopenharmony_ci        }
693cb93a386Sopenharmony_ci        if (fArcButton.fEnabled) {
694cb93a386Sopenharmony_ci            draw_control(canvas, fRadiusControl, fRadius, 0, 500, "radius");
695cb93a386Sopenharmony_ci        }
696cb93a386Sopenharmony_ci#ifdef SK_DEBUG
697cb93a386Sopenharmony_ci        draw_control(canvas, fErrorControl, gDebugStrokerError, kStrokerErrorMin, kStrokerErrorMax,
698cb93a386Sopenharmony_ci                "error");
699cb93a386Sopenharmony_ci#endif
700cb93a386Sopenharmony_ci        draw_control(canvas, fWidthControl, fWidth * fWidthScale, kWidthMin * fWidthScale,
701cb93a386Sopenharmony_ci                kWidthMax * fWidthScale, "width");
702cb93a386Sopenharmony_ci        draw_button(canvas, fQuadButton);
703cb93a386Sopenharmony_ci        draw_button(canvas, fCubicButton);
704cb93a386Sopenharmony_ci        draw_button(canvas, fConicButton);
705cb93a386Sopenharmony_ci        draw_button(canvas, fArcButton);
706cb93a386Sopenharmony_ci        draw_button(canvas, fRRectButton);
707cb93a386Sopenharmony_ci        draw_button(canvas, fCircleButton);
708cb93a386Sopenharmony_ci        draw_button(canvas, fTextButton);
709cb93a386Sopenharmony_ci    }
710cb93a386Sopenharmony_ci
711cb93a386Sopenharmony_ci    class MyClick : public Click {
712cb93a386Sopenharmony_ci    public:
713cb93a386Sopenharmony_ci        int fIndex;
714cb93a386Sopenharmony_ci        MyClick(int index) : fIndex(index) {}
715cb93a386Sopenharmony_ci    };
716cb93a386Sopenharmony_ci
717cb93a386Sopenharmony_ci    Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
718cb93a386Sopenharmony_ci        for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); ++i) {
719cb93a386Sopenharmony_ci            if (hittest(fPts[i], x, y)) {
720cb93a386Sopenharmony_ci                return new MyClick((int)i);
721cb93a386Sopenharmony_ci            }
722cb93a386Sopenharmony_ci        }
723cb93a386Sopenharmony_ci        const SkRect& rectPt = SkRect::MakeXYWH(x, y, 1, 1);
724cb93a386Sopenharmony_ci        if (fWeightControl.contains(rectPt)) {
725cb93a386Sopenharmony_ci            return new MyClick((int) SK_ARRAY_COUNT(fPts) + 1);
726cb93a386Sopenharmony_ci        }
727cb93a386Sopenharmony_ci        if (fRadiusControl.contains(rectPt)) {
728cb93a386Sopenharmony_ci            return new MyClick((int) SK_ARRAY_COUNT(fPts) + 2);
729cb93a386Sopenharmony_ci        }
730cb93a386Sopenharmony_ci#ifdef SK_DEBUG
731cb93a386Sopenharmony_ci        if (fErrorControl.contains(rectPt)) {
732cb93a386Sopenharmony_ci            return new MyClick((int) SK_ARRAY_COUNT(fPts) + 3);
733cb93a386Sopenharmony_ci        }
734cb93a386Sopenharmony_ci#endif
735cb93a386Sopenharmony_ci        if (fWidthControl.contains(rectPt)) {
736cb93a386Sopenharmony_ci            return new MyClick((int) SK_ARRAY_COUNT(fPts) + 4);
737cb93a386Sopenharmony_ci        }
738cb93a386Sopenharmony_ci        if (fCubicButton.fBounds.contains(rectPt)) {
739cb93a386Sopenharmony_ci            fCubicButton.fEnabled ^= true;
740cb93a386Sopenharmony_ci            return new MyClick((int) SK_ARRAY_COUNT(fPts) + 5);
741cb93a386Sopenharmony_ci        }
742cb93a386Sopenharmony_ci        if (fConicButton.fBounds.contains(rectPt)) {
743cb93a386Sopenharmony_ci            fConicButton.fEnabled ^= true;
744cb93a386Sopenharmony_ci            return new MyClick((int) SK_ARRAY_COUNT(fPts) + 6);
745cb93a386Sopenharmony_ci        }
746cb93a386Sopenharmony_ci        if (fQuadButton.fBounds.contains(rectPt)) {
747cb93a386Sopenharmony_ci            fQuadButton.fEnabled ^= true;
748cb93a386Sopenharmony_ci            return new MyClick((int) SK_ARRAY_COUNT(fPts) + 7);
749cb93a386Sopenharmony_ci        }
750cb93a386Sopenharmony_ci        if (fArcButton.fBounds.contains(rectPt)) {
751cb93a386Sopenharmony_ci            fArcButton.fEnabled ^= true;
752cb93a386Sopenharmony_ci            return new MyClick((int) SK_ARRAY_COUNT(fPts) + 8);
753cb93a386Sopenharmony_ci        }
754cb93a386Sopenharmony_ci        if (fRRectButton.fBounds.contains(rectPt)) {
755cb93a386Sopenharmony_ci            fRRectButton.fEnabled ^= true;
756cb93a386Sopenharmony_ci            return new MyClick((int) SK_ARRAY_COUNT(fPts) + 9);
757cb93a386Sopenharmony_ci        }
758cb93a386Sopenharmony_ci        if (fCircleButton.fBounds.contains(rectPt)) {
759cb93a386Sopenharmony_ci            bool wasEnabled = fCircleButton.fEnabled;
760cb93a386Sopenharmony_ci            fCircleButton.fEnabled = !fCircleButton.fFill;
761cb93a386Sopenharmony_ci            fCircleButton.fFill = wasEnabled && !fCircleButton.fFill;
762cb93a386Sopenharmony_ci            return new MyClick((int) SK_ARRAY_COUNT(fPts) + 10);
763cb93a386Sopenharmony_ci        }
764cb93a386Sopenharmony_ci        if (fTextButton.fBounds.contains(rectPt)) {
765cb93a386Sopenharmony_ci            fTextButton.fEnabled ^= true;
766cb93a386Sopenharmony_ci            return new MyClick((int) SK_ARRAY_COUNT(fPts) + 11);
767cb93a386Sopenharmony_ci        }
768cb93a386Sopenharmony_ci        return nullptr;
769cb93a386Sopenharmony_ci    }
770cb93a386Sopenharmony_ci
771cb93a386Sopenharmony_ci    static SkScalar MapScreenYtoValue(SkScalar y, const SkRect& control, SkScalar min,
772cb93a386Sopenharmony_ci            SkScalar max) {
773cb93a386Sopenharmony_ci        return (y - control.fTop) / control.height() * (max - min) + min;
774cb93a386Sopenharmony_ci    }
775cb93a386Sopenharmony_ci
776cb93a386Sopenharmony_ci    bool onClick(Click* click) override {
777cb93a386Sopenharmony_ci        int index = ((MyClick*)click)->fIndex;
778cb93a386Sopenharmony_ci        if (index < (int) SK_ARRAY_COUNT(fPts)) {
779cb93a386Sopenharmony_ci            fPts[index].offset(click->fCurr.fX - click->fPrev.fX,
780cb93a386Sopenharmony_ci                               click->fCurr.fY - click->fPrev.fY);
781cb93a386Sopenharmony_ci        } else if (index == (int) SK_ARRAY_COUNT(fPts) + 1) {
782cb93a386Sopenharmony_ci            fWeight = MapScreenYtoValue(click->fCurr.fY, fWeightControl, 0, 5);
783cb93a386Sopenharmony_ci        } else if (index == (int) SK_ARRAY_COUNT(fPts) + 2) {
784cb93a386Sopenharmony_ci            fRadius = MapScreenYtoValue(click->fCurr.fY, fRadiusControl, 0, 500);
785cb93a386Sopenharmony_ci        }
786cb93a386Sopenharmony_ci#ifdef SK_DEBUG
787cb93a386Sopenharmony_ci        else if (index == (int) SK_ARRAY_COUNT(fPts) + 3) {
788cb93a386Sopenharmony_ci            gDebugStrokerError = std::max(FLT_EPSILON, MapScreenYtoValue(click->fCurr.fY,
789cb93a386Sopenharmony_ci                    fErrorControl, kStrokerErrorMin, kStrokerErrorMax));
790cb93a386Sopenharmony_ci            gDebugStrokerErrorSet = true;
791cb93a386Sopenharmony_ci        }
792cb93a386Sopenharmony_ci#endif
793cb93a386Sopenharmony_ci        else if (index == (int) SK_ARRAY_COUNT(fPts) + 4) {
794cb93a386Sopenharmony_ci            fWidth = std::max(FLT_EPSILON, MapScreenYtoValue(click->fCurr.fY, fWidthControl,
795cb93a386Sopenharmony_ci                    kWidthMin, kWidthMax));
796cb93a386Sopenharmony_ci            fAnimate = fWidth <= kWidthMin;
797cb93a386Sopenharmony_ci        }
798cb93a386Sopenharmony_ci        return true;
799cb93a386Sopenharmony_ci    }
800cb93a386Sopenharmony_ci
801cb93a386Sopenharmony_ciprivate:
802cb93a386Sopenharmony_ci    using INHERITED = Sample;
803cb93a386Sopenharmony_ci};
804cb93a386Sopenharmony_ci
805cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////
806cb93a386Sopenharmony_ci
807cb93a386Sopenharmony_ciDEF_SAMPLE( return new QuadStrokerView(); )
808