188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark/*
288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark * Copyright 2012 Google Inc.
388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark *
488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark * Use of this source code is governed by a BSD-style license that can be
588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark * found in the LICENSE file.
688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark */
788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark#include "sk_tool_utils.h"
988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark#include "SampleCode.h"
1088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark#include "SkView.h"
1188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark#include "SkCanvas.h"
12b6474dd1a530a543ae799c3822e8bc60180761c0caryclark#include "SkGeometry.h"
1388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark#include "SkPathMeasure.h"
1488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark#include "SkRandom.h"
1588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark#include "SkRRect.h"
1688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark#include "SkColorPriv.h"
1788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark#include "SkStrokerPriv.h"
1888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark#include "SkSurface.h"
1988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
2088c748aeea1cedfc8280d7f3da3d949ed01be177caryclarkstatic bool hittest(const SkPoint& target, SkScalar x, SkScalar y) {
2188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    const SkScalar TOL = 7;
2288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    return SkPoint::Distance(target, SkPoint::Make(x, y)) <= TOL;
2388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark}
2488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
2588c748aeea1cedfc8280d7f3da3d949ed01be177caryclarkstatic int getOnCurvePoints(const SkPath& path, SkPoint storage[]) {
2688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    SkPath::RawIter iter(path);
2788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    SkPoint pts[4];
2888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    SkPath::Verb verb;
2988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
3088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    int count = 0;
3188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
3288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        switch (verb) {
3388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            case SkPath::kMove_Verb:
3488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            case SkPath::kLine_Verb:
3588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            case SkPath::kQuad_Verb:
3688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            case SkPath::kConic_Verb:
3788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            case SkPath::kCubic_Verb:
3888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                storage[count++] = pts[0];
3988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                break;
4088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            default:
4188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                break;
4288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
4388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    }
4488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    return count;
4588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark}
4688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
4788c748aeea1cedfc8280d7f3da3d949ed01be177caryclarkstatic void getContourCounts(const SkPath& path, SkTArray<int>* contourCounts) {
4888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    SkPath::RawIter iter(path);
4988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    SkPoint pts[4];
5088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    SkPath::Verb verb;
5188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
5288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    int count = 0;
5388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
5488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        switch (verb) {
5588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            case SkPath::kMove_Verb:
5688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            case SkPath::kLine_Verb:
5788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                count += 1;
5888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                break;
5988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            case SkPath::kQuad_Verb:
6088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            case SkPath::kConic_Verb:
6188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                count += 2;
6288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                break;
6388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            case SkPath::kCubic_Verb:
6488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                count += 3;
6588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                break;
6688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            case SkPath::kClose_Verb:
6788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                contourCounts->push_back(count);
6888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                count = 0;
6988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                break;
7088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            default:
7188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                break;
7288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
7388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    }
7488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    if (count > 0) {
7588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        contourCounts->push_back(count);
7688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    }
7788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark}
7888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
7988c748aeea1cedfc8280d7f3da3d949ed01be177caryclarkstatic void erase(SkSurface* surface) {
8088651aeb551fb02003a7600679e8e7df8a589e7fcaryclark    SkCanvas* canvas = surface->getCanvas();
8188651aeb551fb02003a7600679e8e7df8a589e7fcaryclark    if (canvas) {
8288651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        canvas->clear(SK_ColorTRANSPARENT);
8388651aeb551fb02003a7600679e8e7df8a589e7fcaryclark    }
8488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark}
8588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
8688c748aeea1cedfc8280d7f3da3d949ed01be177caryclarkstruct StrokeTypeButton {
8788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    SkRect fBounds;
8888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    char fLabel;
8988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    bool fEnabled;
9088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark};
9188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
9204e4d08556750ff6be4576a4cd4925964c63376fcaryclarkstruct CircleTypeButton : public StrokeTypeButton {
9304e4d08556750ff6be4576a4cd4925964c63376fcaryclark    bool fFill;
9404e4d08556750ff6be4576a4cd4925964c63376fcaryclark};
9504e4d08556750ff6be4576a4cd4925964c63376fcaryclark
9688c748aeea1cedfc8280d7f3da3d949ed01be177caryclarkclass QuadStrokerView : public SampleView {
9788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    enum {
9888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SKELETON_COLOR = 0xFF0000FF,
9988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        WIREFRAME_COLOR = 0x80FF0000
10088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    };
10188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
10288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    enum {
10388651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        kCount = 18
10488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    };
10588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    SkPoint fPts[kCount];
10604e4d08556750ff6be4576a4cd4925964c63376fcaryclark    SkRect fWeightControl;
10788651aeb551fb02003a7600679e8e7df8a589e7fcaryclark    SkRect fRadiusControl;
10888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    SkRect fErrorControl;
10988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    SkRect fWidthControl;
11088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    SkRect fBounds;
11188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    SkMatrix fMatrix, fInverse;
11288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    SkAutoTUnref<SkShader> fShader;
11388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    SkAutoTUnref<SkSurface> fMinSurface;
11488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    SkAutoTUnref<SkSurface> fMaxSurface;
11588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    StrokeTypeButton fCubicButton;
11604e4d08556750ff6be4576a4cd4925964c63376fcaryclark    StrokeTypeButton fConicButton;
11788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    StrokeTypeButton fQuadButton;
11888651aeb551fb02003a7600679e8e7df8a589e7fcaryclark    StrokeTypeButton fArcButton;
11988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    StrokeTypeButton fRRectButton;
12004e4d08556750ff6be4576a4cd4925964c63376fcaryclark    CircleTypeButton fCircleButton;
12188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    StrokeTypeButton fTextButton;
12288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    SkString fText;
12388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    SkScalar fTextSize;
12404e4d08556750ff6be4576a4cd4925964c63376fcaryclark    SkScalar fWeight;
12588651aeb551fb02003a7600679e8e7df8a589e7fcaryclark    SkScalar fRadius;
12688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    SkScalar fWidth, fDWidth;
12788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    SkScalar fWidthScale;
12888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    int fW, fH, fZoom;
12988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    bool fAnimate;
13088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    bool fDrawRibs;
13188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    bool fDrawTangents;
132b6474dd1a530a543ae799c3822e8bc60180761c0caryclark    bool fDrawTDivs;
133a76b7a3b04bc0e937921a1eb38d713c619e60e4ecaryclark#ifdef SK_DEBUG
13488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    #define kStrokerErrorMin 0.001f
13588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    #define kStrokerErrorMax 5
13688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark#endif
13788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    #define kWidthMin 1
13888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    #define kWidthMax 100
13988c748aeea1cedfc8280d7f3da3d949ed01be177caryclarkpublic:
14088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    QuadStrokerView() {
14188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        this->setBGColor(SK_ColorLTGRAY);
14288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
14304e4d08556750ff6be4576a4cd4925964c63376fcaryclark        fPts[0].set(50, 200);  // cubic
14488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fPts[1].set(50, 100);
14588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fPts[2].set(150, 50);
14688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fPts[3].set(300, 50);
14788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
14804e4d08556750ff6be4576a4cd4925964c63376fcaryclark        fPts[4].set(350, 200);  // conic
14988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fPts[5].set(350, 100);
15088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fPts[6].set(450, 50);
15188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
15204e4d08556750ff6be4576a4cd4925964c63376fcaryclark        fPts[7].set(150, 300);  // quad
15304e4d08556750ff6be4576a4cd4925964c63376fcaryclark        fPts[8].set(150, 200);
15404e4d08556750ff6be4576a4cd4925964c63376fcaryclark        fPts[9].set(250, 150);
15504e4d08556750ff6be4576a4cd4925964c63376fcaryclark
15688651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        fPts[10].set(250, 200);  // arc
15788651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        fPts[11].set(250, 300);
15888651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        fPts[12].set(150, 350);
15988651aeb551fb02003a7600679e8e7df8a589e7fcaryclark
16088651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        fPts[13].set(200, 200); // rrect
16188651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        fPts[14].set(400, 400);
16204e4d08556750ff6be4576a4cd4925964c63376fcaryclark
16388651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        fPts[15].set(250, 250);  // oval
16488651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        fPts[16].set(450, 450);
16588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
16688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fText = "a";
16788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fTextSize = 12;
16888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fWidth = 50;
16988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fDWidth = 0.25f;
17004e4d08556750ff6be4576a4cd4925964c63376fcaryclark        fWeight = 1;
17188651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        fRadius = 150;
17288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
17388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fCubicButton.fLabel = 'C';
17488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fCubicButton.fEnabled = false;
17504e4d08556750ff6be4576a4cd4925964c63376fcaryclark        fConicButton.fLabel = 'K';
17688651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        fConicButton.fEnabled = false;
17788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fQuadButton.fLabel = 'Q';
17888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fQuadButton.fEnabled = false;
17988651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        fArcButton.fLabel = 'A';
18088651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        fArcButton.fEnabled = true;
18188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fRRectButton.fLabel = 'R';
18288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fRRectButton.fEnabled = false;
18304e4d08556750ff6be4576a4cd4925964c63376fcaryclark        fCircleButton.fLabel = 'O';
18488651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        fCircleButton.fEnabled = true;
18588651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        fCircleButton.fFill = true;
18688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fTextButton.fLabel = 'T';
18704e4d08556750ff6be4576a4cd4925964c63376fcaryclark        fTextButton.fEnabled = false;
18888651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        fAnimate = false;
18988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        setAsNeeded();
19088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    }
19188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
19288c748aeea1cedfc8280d7f3da3d949ed01be177caryclarkprotected:
19336352bf5e38f45a70ee4f4fc132a38048d38206dmtklein    bool onQuery(SkEvent* evt) override {
19488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        if (SampleCode::TitleQ(*evt)) {
19588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            SampleCode::TitleR(evt, "QuadStroker");
19688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            return true;
19788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
19888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkUnichar uni;
19988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        if (fTextButton.fEnabled && SampleCode::CharQ(*evt, &uni)) {
20088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            switch (uni) {
20188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                case ' ':
20288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                    fText = "";
20388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                    break;
20488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                case '-':
20588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                    fTextSize = SkTMax(1.0f, fTextSize - 1);
20688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                    break;
20788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                case '+':
20888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                case '=':
20988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                    fTextSize += 1;
21088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                    break;
21188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                default:
21288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                    fText.appendUnichar(uni);
21388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            }
21496fcdcc219d2a0d3579719b84b28bede76efba64halcanary            this->inval(nullptr);
21588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            return true;
21688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
21788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        return this->INHERITED::onQuery(evt);
21888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    }
21988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
22036352bf5e38f45a70ee4f4fc132a38048d38206dmtklein    void onSizeChange() override {
22188651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        fRadiusControl.setXYWH(this->width() - 200, 30, 30, 400);
22204e4d08556750ff6be4576a4cd4925964c63376fcaryclark        fWeightControl.setXYWH(this->width() - 150, 30, 30, 400);
22388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fErrorControl.setXYWH(this->width() - 100, 30, 30, 400);
22488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fWidthControl.setXYWH(this->width() -  50, 30, 30, 400);
22504e4d08556750ff6be4576a4cd4925964c63376fcaryclark        int buttonOffset = 450;
22604e4d08556750ff6be4576a4cd4925964c63376fcaryclark        fCubicButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
22704e4d08556750ff6be4576a4cd4925964c63376fcaryclark        buttonOffset += 50;
22804e4d08556750ff6be4576a4cd4925964c63376fcaryclark        fConicButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
22904e4d08556750ff6be4576a4cd4925964c63376fcaryclark        buttonOffset += 50;
23004e4d08556750ff6be4576a4cd4925964c63376fcaryclark        fQuadButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
23104e4d08556750ff6be4576a4cd4925964c63376fcaryclark        buttonOffset += 50;
23288651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        fArcButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
23388651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        buttonOffset += 50;
23404e4d08556750ff6be4576a4cd4925964c63376fcaryclark        fRRectButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
23504e4d08556750ff6be4576a4cd4925964c63376fcaryclark        buttonOffset += 50;
23604e4d08556750ff6be4576a4cd4925964c63376fcaryclark        fCircleButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
23704e4d08556750ff6be4576a4cd4925964c63376fcaryclark        buttonOffset += 50;
23804e4d08556750ff6be4576a4cd4925964c63376fcaryclark        fTextButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
23988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        this->INHERITED::onSizeChange();
24088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    }
24188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
24288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark     void copyMinToMax() {
24388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        erase(fMaxSurface);
24488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkCanvas* canvas = fMaxSurface->getCanvas();
24588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        canvas->save();
24688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        canvas->concat(fMatrix);
24796fcdcc219d2a0d3579719b84b28bede76efba64halcanary        fMinSurface->draw(canvas, 0, 0, nullptr);
24888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        canvas->restore();
24988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
25088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkPaint paint;
25188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setXfermodeMode(SkXfermode::kClear_Mode);
25288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        for (int iy = 1; iy < fH; ++iy) {
25388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            SkScalar y = SkIntToScalar(iy * fZoom);
25488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            canvas->drawLine(0, y - SK_ScalarHalf, 999, y - SK_ScalarHalf, paint);
25588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
25688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        for (int ix = 1; ix < fW; ++ix) {
25788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            SkScalar x = SkIntToScalar(ix * fZoom);
25888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            canvas->drawLine(x - SK_ScalarHalf, 0, x - SK_ScalarHalf, 999, paint);
25988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
26088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    }
26188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
26288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark   void setWHZ(int width, int height, int zoom) {
26388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fZoom = zoom;
26488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fBounds.set(0, 0, SkIntToScalar(width * zoom), SkIntToScalar(height * zoom));
26588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fMatrix.setScale(SkIntToScalar(zoom), SkIntToScalar(zoom));
26688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fInverse.setScale(SK_Scalar1 / zoom, SK_Scalar1 / zoom);
26788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fShader.reset(sk_tool_utils::create_checkerboard_shader(
26888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                              0xFFCCCCCC, 0xFFFFFFFF, zoom));
26988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
27088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
27188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fMinSurface.reset(SkSurface::NewRaster(info));
27288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        info = info.makeWH(width * zoom, height * zoom);
27388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fMaxSurface.reset(SkSurface::NewRaster(info));
27488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    }
27588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
27688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    void draw_points(SkCanvas* canvas, const SkPath& path, SkColor color,
27788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                     bool show_lines) {
27888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkPaint paint;
27988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setColor(color);
28088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setAlpha(0x80);
28188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setAntiAlias(true);
28288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        int n = path.countPoints();
28388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkAutoSTArray<32, SkPoint> pts(n);
28488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        if (show_lines && fDrawTangents) {
28588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            SkTArray<int> contourCounts;
28688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            getContourCounts(path, &contourCounts);
28788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            SkPoint* ptPtr = pts.get();
28888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            for (int i = 0; i < contourCounts.count(); ++i) {
28988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                int count = contourCounts[i];
29088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                path.getPoints(ptPtr, count);
29188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                canvas->drawPoints(SkCanvas::kPolygon_PointMode, count, ptPtr, paint);
29288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                ptPtr += count;
29388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            }
29488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        } else {
29588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            n = getOnCurvePoints(path, pts.get());
29688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
29788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setStrokeWidth(5);
29888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts.get(), paint);
29988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    }
30088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
30188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    void draw_ribs(SkCanvas* canvas, const SkPath& path, SkScalar width,
30288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                   SkColor color) {
30388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        const SkScalar radius = width / 2;
30488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
30588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkPathMeasure meas(path, false);
30688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkScalar total = meas.getLength();
30788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
30888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkScalar delta = 8;
309b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        SkPaint paint, labelP;
31088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setColor(color);
311b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        labelP.setColor(color & 0xff5f9f5f);
31288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkPoint pos, tan;
313b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        int index = 0;
31488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        for (SkScalar dist = 0; dist <= total; dist += delta) {
31588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            if (meas.getPosTan(dist, &pos, &tan)) {
31688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                tan.scale(radius);
31788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                tan.rotateCCW();
31888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                canvas->drawLine(pos.x() + tan.x(), pos.y() + tan.y(),
31988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                                 pos.x() - tan.x(), pos.y() - tan.y(), paint);
320b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                if (0 == index % 10) {
321b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    SkString label;
322b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    label.appendS32(index);
323b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    SkRect dot = SkRect::MakeXYWH(pos.x() - 2, pos.y() - 2, 4, 4);
324b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    canvas->drawRect(dot, labelP);
325b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    canvas->drawText(label.c_str(), label.size(),
326b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                        pos.x() - tan.x() * 1.25f, pos.y() - tan.y() * 1.25f, labelP);
327b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                }
328b6474dd1a530a543ae799c3822e8bc60180761c0caryclark            }
329b6474dd1a530a543ae799c3822e8bc60180761c0caryclark            ++index;
330b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        }
331b6474dd1a530a543ae799c3822e8bc60180761c0caryclark    }
332b6474dd1a530a543ae799c3822e8bc60180761c0caryclark
333b6474dd1a530a543ae799c3822e8bc60180761c0caryclark    void draw_t_divs(SkCanvas* canvas, const SkPath& path, SkScalar width, SkColor color) {
334b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        const SkScalar radius = width / 2;
335b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        SkPaint paint;
336b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        paint.setColor(color);
337b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        SkPathMeasure meas(path, false);
338b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        SkScalar total = meas.getLength();
339b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        SkScalar delta = 8;
340b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        int ribs = 0;
341b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        for (SkScalar dist = 0; dist <= total; dist += delta) {
342b6474dd1a530a543ae799c3822e8bc60180761c0caryclark            ++ribs;
343b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        }
344b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        SkPath::RawIter iter(path);
345b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        SkPoint pts[4];
346b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        if (SkPath::kMove_Verb != iter.next(pts)) {
347b6474dd1a530a543ae799c3822e8bc60180761c0caryclark            SkASSERT(0);
348b6474dd1a530a543ae799c3822e8bc60180761c0caryclark            return;
349b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        }
350b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        SkPath::Verb verb = iter.next(pts);
351b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        SkASSERT(SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb);
352b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        SkPoint pos, tan;
353b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        for (int index = 0; index < ribs; ++index) {
354b6474dd1a530a543ae799c3822e8bc60180761c0caryclark            SkScalar t = (SkScalar) index / ribs;
355b6474dd1a530a543ae799c3822e8bc60180761c0caryclark            switch (verb) {
356b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                case SkPath::kLine_Verb:
357b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    tan = pts[1] - pts[0];
358b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    pos = pts[0];
359b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    pos.fX += tan.fX * t;
360b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    pos.fY += tan.fY * t;
361b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    break;
362b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                case SkPath::kQuad_Verb:
363b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    pos = SkEvalQuadAt(pts, t);
364b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    tan = SkEvalQuadTangentAt(pts, t);
365b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    break;
366b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                case SkPath::kConic_Verb: {
367b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    SkConic conic(pts, iter.conicWeight());
368b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    pos = conic.evalAt(t);
369b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    tan = conic.evalTangentAt(t);
370b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    } break;
371b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                case SkPath::kCubic_Verb:
372b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    SkEvalCubicAt(pts, t, &pos, &tan, nullptr);
373b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    break;
374b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                default:
375b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    SkASSERT(0);
376b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    return;
377b6474dd1a530a543ae799c3822e8bc60180761c0caryclark            }
378b6474dd1a530a543ae799c3822e8bc60180761c0caryclark            tan.setLength(radius);
379b6474dd1a530a543ae799c3822e8bc60180761c0caryclark            tan.rotateCCW();
380b6474dd1a530a543ae799c3822e8bc60180761c0caryclark            canvas->drawLine(pos.x() + tan.x(), pos.y() + tan.y(),
381b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                                pos.x() - tan.x(), pos.y() - tan.y(), paint);
382b6474dd1a530a543ae799c3822e8bc60180761c0caryclark            if (0 == index % 10) {
383b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                SkString label;
384b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                label.appendS32(index);
385b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                canvas->drawText(label.c_str(), label.size(),
386b6474dd1a530a543ae799c3822e8bc60180761c0caryclark                    pos.x() + tan.x() * 1.25f, pos.y() + tan.y() * 1.25f, paint);
38788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            }
38888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
38988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    }
39088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
39104e4d08556750ff6be4576a4cd4925964c63376fcaryclark    void draw_stroke(SkCanvas* canvas, const SkPath& path, SkScalar width, SkScalar scale,
39204e4d08556750ff6be4576a4cd4925964c63376fcaryclark            bool drawText) {
393612f70d5fa2d4bf7a393c791966bbce933407017caryclark        if (path.isEmpty()) {
39488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            return;
39588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
396612f70d5fa2d4bf7a393c791966bbce933407017caryclark        SkRect bounds = path.getBounds();
39704e4d08556750ff6be4576a4cd4925964c63376fcaryclark        this->setWHZ(SkScalarCeilToInt(bounds.right()), drawText
39804e4d08556750ff6be4576a4cd4925964c63376fcaryclark                ? SkScalarRoundToInt(scale * 3 / 2) : SkScalarRoundToInt(scale),
39904e4d08556750ff6be4576a4cd4925964c63376fcaryclark                SkScalarRoundToInt(950.0f / scale));
40088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        erase(fMinSurface);
40188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkPaint paint;
40288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setColor(0x1f1f0f0f);
40388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setStyle(SkPaint::kStroke_Style);
40404e4d08556750ff6be4576a4cd4925964c63376fcaryclark        paint.setStrokeWidth(width * scale * scale);
40588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setColor(0x3f0f1f3f);
40604e4d08556750ff6be4576a4cd4925964c63376fcaryclark        if (drawText) {
40704e4d08556750ff6be4576a4cd4925964c63376fcaryclark            fMinSurface->getCanvas()->drawPath(path, paint);
40804e4d08556750ff6be4576a4cd4925964c63376fcaryclark            this->copyMinToMax();
40996fcdcc219d2a0d3579719b84b28bede76efba64halcanary            fMaxSurface->draw(canvas, 0, 0, nullptr);
41004e4d08556750ff6be4576a4cd4925964c63376fcaryclark        }
41188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setAntiAlias(true);
41288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setStyle(SkPaint::kStroke_Style);
41388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setStrokeWidth(1);
41488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
41588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setColor(SKELETON_COLOR);
41688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkPath scaled;
41788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkMatrix matrix;
41888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        matrix.reset();
41904e4d08556750ff6be4576a4cd4925964c63376fcaryclark        matrix.setScale(950 / scale, 950 / scale);
42088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        if (drawText) {
42188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            path.transform(matrix, &scaled);
42288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        } else {
42388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            scaled = path;
42488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
42588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        canvas->drawPath(scaled, paint);
42688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        draw_points(canvas, scaled, SKELETON_COLOR, true);
42788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
42888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        if (fDrawRibs) {
42988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            draw_ribs(canvas, scaled, width, 0xFF00FF00);
43088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
43188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
432b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        if (fDrawTDivs) {
433b6474dd1a530a543ae799c3822e8bc60180761c0caryclark            draw_t_divs(canvas, scaled, width, 0xFF3F3F00);
434b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        }
435b6474dd1a530a543ae799c3822e8bc60180761c0caryclark
43688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkPath fill;
43788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
43888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkPaint p;
43988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        p.setStyle(SkPaint::kStroke_Style);
44004e4d08556750ff6be4576a4cd4925964c63376fcaryclark        if (drawText) {
44104e4d08556750ff6be4576a4cd4925964c63376fcaryclark            p.setStrokeWidth(width * scale * scale);
44204e4d08556750ff6be4576a4cd4925964c63376fcaryclark        } else {
44304e4d08556750ff6be4576a4cd4925964c63376fcaryclark            p.setStrokeWidth(width);
44404e4d08556750ff6be4576a4cd4925964c63376fcaryclark        }
44588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        p.getFillPath(path, &fill);
44688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkPath scaledFill;
44788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        if (drawText) {
44888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            fill.transform(matrix, &scaledFill);
44988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        } else {
45088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            scaledFill = fill;
45188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
45288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setColor(WIREFRAME_COLOR);
45388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        canvas->drawPath(scaledFill, paint);
45488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        draw_points(canvas, scaledFill, WIREFRAME_COLOR, false);
45588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    }
45688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
45704e4d08556750ff6be4576a4cd4925964c63376fcaryclark    void draw_fill(SkCanvas* canvas, const SkRect& rect, SkScalar width) {
45804e4d08556750ff6be4576a4cd4925964c63376fcaryclark        if (rect.isEmpty()) {
45904e4d08556750ff6be4576a4cd4925964c63376fcaryclark            return;
46004e4d08556750ff6be4576a4cd4925964c63376fcaryclark        }
46104e4d08556750ff6be4576a4cd4925964c63376fcaryclark        SkPaint paint;
46204e4d08556750ff6be4576a4cd4925964c63376fcaryclark        paint.setColor(0x1f1f0f0f);
46304e4d08556750ff6be4576a4cd4925964c63376fcaryclark        paint.setStyle(SkPaint::kStroke_Style);
46404e4d08556750ff6be4576a4cd4925964c63376fcaryclark        paint.setStrokeWidth(width);
46504e4d08556750ff6be4576a4cd4925964c63376fcaryclark        SkPath path;
46604e4d08556750ff6be4576a4cd4925964c63376fcaryclark        SkScalar maxSide = SkTMax(rect.width(), rect.height()) / 2;
46704e4d08556750ff6be4576a4cd4925964c63376fcaryclark        SkPoint center = { rect.fLeft + maxSide, rect.fTop + maxSide };
46804e4d08556750ff6be4576a4cd4925964c63376fcaryclark        path.addCircle(center.fX, center.fY, maxSide);
46904e4d08556750ff6be4576a4cd4925964c63376fcaryclark        canvas->drawPath(path, paint);
47004e4d08556750ff6be4576a4cd4925964c63376fcaryclark        paint.setStyle(SkPaint::kFill_Style);
47104e4d08556750ff6be4576a4cd4925964c63376fcaryclark        path.reset();
47204e4d08556750ff6be4576a4cd4925964c63376fcaryclark        path.addCircle(center.fX, center.fY, maxSide - width / 2);
47304e4d08556750ff6be4576a4cd4925964c63376fcaryclark        paint.setColor(0x3f0f1f3f);
47404e4d08556750ff6be4576a4cd4925964c63376fcaryclark        canvas->drawPath(path, paint);
47504e4d08556750ff6be4576a4cd4925964c63376fcaryclark        path.reset();
47604e4d08556750ff6be4576a4cd4925964c63376fcaryclark        path.setFillType(SkPath::kEvenOdd_FillType);
47704e4d08556750ff6be4576a4cd4925964c63376fcaryclark        path.addCircle(center.fX, center.fY, maxSide + width / 2);
47804e4d08556750ff6be4576a4cd4925964c63376fcaryclark        SkRect outside = SkRect::MakeXYWH(center.fX - maxSide - width, center.fY - maxSide - width,
47904e4d08556750ff6be4576a4cd4925964c63376fcaryclark                (maxSide + width) * 2, (maxSide + width) * 2);
48004e4d08556750ff6be4576a4cd4925964c63376fcaryclark        path.addRect(outside);
48104e4d08556750ff6be4576a4cd4925964c63376fcaryclark        canvas->drawPath(path, paint);
48204e4d08556750ff6be4576a4cd4925964c63376fcaryclark    }
48304e4d08556750ff6be4576a4cd4925964c63376fcaryclark
48488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    void draw_button(SkCanvas* canvas, const StrokeTypeButton& button) {
48588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkPaint paint;
48688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setAntiAlias(true);
48788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setStyle(SkPaint::kStroke_Style);
48888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setColor(button.fEnabled ? 0xFF3F0000 : 0x6F3F0000);
48988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        canvas->drawRect(button.fBounds, paint);
49088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setTextSize(25.0f);
49188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setColor(button.fEnabled ? 0xFF3F0000 : 0x6F3F0000);
49288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setTextAlign(SkPaint::kCenter_Align);
49388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setStyle(SkPaint::kFill_Style);
49488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        canvas->drawText(&button.fLabel, 1, button.fBounds.centerX(), button.fBounds.fBottom - 5,
49588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                paint);
49688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    }
49788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
49888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    void draw_control(SkCanvas* canvas, const SkRect& bounds, SkScalar value,
49988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            SkScalar min, SkScalar max, const char* name) {
50088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkPaint paint;
50188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setAntiAlias(true);
50288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setStyle(SkPaint::kStroke_Style);
50388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        canvas->drawRect(bounds, paint);
50488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkScalar scale = max - min;
50588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkScalar yPos = bounds.fTop + (value - min) * bounds.height() / scale;
50688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setColor(0xFFFF0000);
50788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        canvas->drawLine(bounds.fLeft - 5, yPos, bounds.fRight + 5, yPos, paint);
50888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkString label;
50988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        label.printf("%0.3g", value);
51088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setColor(0xFF000000);
51188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setTextSize(11.0f);
51288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setStyle(SkPaint::kFill_Style);
51388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        canvas->drawText(label.c_str(), label.size(), bounds.fLeft + 5, yPos - 5, paint);
51488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        paint.setTextSize(13.0f);
51588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        canvas->drawText(name, strlen(name), bounds.fLeft, bounds.bottom() + 11, paint);
51688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    }
51788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
51888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    void setForGeometry() {
51988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fDrawRibs = true;
52088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fDrawTangents = true;
521b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        fDrawTDivs = false;
52288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fWidthScale = 1;
52388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    }
52488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
52588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    void setForText() {
526b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        fDrawRibs = fDrawTangents = fDrawTDivs = false;
52788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        fWidthScale = 0.002f;
52888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    }
52988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
530b6474dd1a530a543ae799c3822e8bc60180761c0caryclark    void setForSingles() {
531b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        setForGeometry();
532b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        fDrawTDivs = true;
533b6474dd1a530a543ae799c3822e8bc60180761c0caryclark    }
534b6474dd1a530a543ae799c3822e8bc60180761c0caryclark
53588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    void setAsNeeded() {
536b6474dd1a530a543ae799c3822e8bc60180761c0caryclark        if (fConicButton.fEnabled || fCubicButton.fEnabled || fQuadButton.fEnabled) {
537b6474dd1a530a543ae799c3822e8bc60180761c0caryclark            setForSingles();
53888651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        } else if (fRRectButton.fEnabled || fCircleButton.fEnabled || fArcButton.fEnabled) {
53988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            setForGeometry();
54088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        } else {
54188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            setForText();
54288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
54388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    }
54488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
54588651aeb551fb02003a7600679e8e7df8a589e7fcaryclark    bool arcCenter(SkPoint* center) {
54688651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        SkPath path;
54788651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        path.moveTo(fPts[10]);
54888651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        path.arcTo(fPts[11], fPts[12], fRadius);
54988651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        SkPath::Iter iter(path, false);
55088651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        SkPoint pts[4];
55188651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        iter.next(pts);
55288651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        if (SkPath::kLine_Verb == iter.next(pts)) {
55388651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            iter.next(pts);
55488651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        }
55588651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        SkVector before = pts[0] - pts[1];
55688651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        SkVector after = pts[1] - pts[2];
55788651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        before.setLength(fRadius);
55888651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        after.setLength(fRadius);
55988651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        SkVector beforeCCW, afterCCW;
56088651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        before.rotateCCW(&beforeCCW);
56188651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        after.rotateCCW(&afterCCW);
56288651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        beforeCCW += pts[0];
56388651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        afterCCW += pts[2];
56488651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        *center = beforeCCW;
56588651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        if (SkScalarNearlyEqual(beforeCCW.fX, afterCCW.fX)
56688651aeb551fb02003a7600679e8e7df8a589e7fcaryclark                && SkScalarNearlyEqual(beforeCCW.fY, afterCCW.fY)) {
56788651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            return true;
56888651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        }
56988651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        SkVector beforeCW, afterCW;
57088651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        before.rotateCW(&beforeCW);
57188651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        after.rotateCW(&afterCW);
57288651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        beforeCW += pts[0];
57388651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        afterCW += pts[2];
57488651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        *center = beforeCW;
57588651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        return SkScalarNearlyEqual(beforeCW.fX, afterCW.fX)
57688651aeb551fb02003a7600679e8e7df8a589e7fcaryclark                && SkScalarNearlyEqual(beforeCCW.fY, afterCW.fY);
57788651aeb551fb02003a7600679e8e7df8a589e7fcaryclark    }
57888651aeb551fb02003a7600679e8e7df8a589e7fcaryclark
57936352bf5e38f45a70ee4f4fc132a38048d38206dmtklein    void onDrawContent(SkCanvas* canvas) override {
58088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkPath path;
58188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        SkScalar width = fWidth;
58288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
58388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        if (fCubicButton.fEnabled) {
58488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            path.moveTo(fPts[0]);
58588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            path.cubicTo(fPts[1], fPts[2], fPts[3]);
586b6474dd1a530a543ae799c3822e8bc60180761c0caryclark            setForSingles();
58704e4d08556750ff6be4576a4cd4925964c63376fcaryclark            draw_stroke(canvas, path, width, 950, false);
58804e4d08556750ff6be4576a4cd4925964c63376fcaryclark        }
58904e4d08556750ff6be4576a4cd4925964c63376fcaryclark
59004e4d08556750ff6be4576a4cd4925964c63376fcaryclark        if (fConicButton.fEnabled) {
591b6474dd1a530a543ae799c3822e8bc60180761c0caryclark            path.reset();
59204e4d08556750ff6be4576a4cd4925964c63376fcaryclark            path.moveTo(fPts[4]);
59304e4d08556750ff6be4576a4cd4925964c63376fcaryclark            path.conicTo(fPts[5], fPts[6], fWeight);
594b6474dd1a530a543ae799c3822e8bc60180761c0caryclark            setForSingles();
59504e4d08556750ff6be4576a4cd4925964c63376fcaryclark            draw_stroke(canvas, path, width, 950, false);
59688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
59788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
59888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        if (fQuadButton.fEnabled) {
59988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            path.reset();
60004e4d08556750ff6be4576a4cd4925964c63376fcaryclark            path.moveTo(fPts[7]);
60104e4d08556750ff6be4576a4cd4925964c63376fcaryclark            path.quadTo(fPts[8], fPts[9]);
602b6474dd1a530a543ae799c3822e8bc60180761c0caryclark            setForSingles();
60304e4d08556750ff6be4576a4cd4925964c63376fcaryclark            draw_stroke(canvas, path, width, 950, false);
60488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
60588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
60688651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        if (fArcButton.fEnabled) {
60788651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            path.reset();
60888651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            path.moveTo(fPts[10]);
60988651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            path.arcTo(fPts[11], fPts[12], fRadius);
61088651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            setForGeometry();
61188651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            draw_stroke(canvas, path, width, 950, false);
61288651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            SkPath pathPts;
61388651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            pathPts.moveTo(fPts[10]);
61488651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            pathPts.lineTo(fPts[11]);
61588651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            pathPts.lineTo(fPts[12]);
61688651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            draw_points(canvas, pathPts, SK_ColorDKGRAY, true);
61788651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        }
61888651aeb551fb02003a7600679e8e7df8a589e7fcaryclark
61988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        if (fRRectButton.fEnabled) {
62088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            SkScalar rad = 32;
62188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            SkRect r;
62288651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            r.set(&fPts[13], 2);
62388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            path.reset();
62488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            SkRRect rr;
62588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            rr.setRectXY(r, rad, rad);
62688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            path.addRRect(rr);
62788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            setForGeometry();
62804e4d08556750ff6be4576a4cd4925964c63376fcaryclark            draw_stroke(canvas, path, width, 950, false);
62988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
63088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            path.reset();
63188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            SkRRect rr2;
63288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            rr.inset(width/2, width/2, &rr2);
63388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            path.addRRect(rr2, SkPath::kCCW_Direction);
63488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            rr.inset(-width/2, -width/2, &rr2);
63588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            path.addRRect(rr2, SkPath::kCW_Direction);
63688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            SkPaint paint;
63788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            paint.setAntiAlias(true);
63888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            paint.setColor(0x40FF8844);
63988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            canvas->drawPath(path, paint);
64088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
64188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
64204e4d08556750ff6be4576a4cd4925964c63376fcaryclark        if (fCircleButton.fEnabled) {
64304e4d08556750ff6be4576a4cd4925964c63376fcaryclark            path.reset();
64404e4d08556750ff6be4576a4cd4925964c63376fcaryclark            SkRect r;
64588651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            r.set(&fPts[15], 2);
64604e4d08556750ff6be4576a4cd4925964c63376fcaryclark            path.addOval(r);
64704e4d08556750ff6be4576a4cd4925964c63376fcaryclark            setForGeometry();
64804e4d08556750ff6be4576a4cd4925964c63376fcaryclark            if (fCircleButton.fFill) {
64988651aeb551fb02003a7600679e8e7df8a589e7fcaryclark                if (fArcButton.fEnabled) {
65088651aeb551fb02003a7600679e8e7df8a589e7fcaryclark                    SkPoint center;
65188651aeb551fb02003a7600679e8e7df8a589e7fcaryclark                    if (arcCenter(&center)) {
65288651aeb551fb02003a7600679e8e7df8a589e7fcaryclark                        r.set(center.fX - fRadius, center.fY - fRadius, center.fX + fRadius,
65388651aeb551fb02003a7600679e8e7df8a589e7fcaryclark                                center.fY + fRadius);
65488651aeb551fb02003a7600679e8e7df8a589e7fcaryclark                    }
65588651aeb551fb02003a7600679e8e7df8a589e7fcaryclark                }
65604e4d08556750ff6be4576a4cd4925964c63376fcaryclark                draw_fill(canvas, r, width);
65704e4d08556750ff6be4576a4cd4925964c63376fcaryclark            } else {
65804e4d08556750ff6be4576a4cd4925964c63376fcaryclark                draw_stroke(canvas, path, width, 950, false);
65904e4d08556750ff6be4576a4cd4925964c63376fcaryclark            }
66004e4d08556750ff6be4576a4cd4925964c63376fcaryclark        }
66104e4d08556750ff6be4576a4cd4925964c63376fcaryclark
66288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        if (fTextButton.fEnabled) {
66388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            path.reset();
66488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            SkPaint paint;
66588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            paint.setAntiAlias(true);
66688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            paint.setTextSize(fTextSize);
66788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            paint.getTextPath(fText.c_str(), fText.size(), 0, fTextSize, &path);
66888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            setForText();
66904e4d08556750ff6be4576a4cd4925964c63376fcaryclark            draw_stroke(canvas, path, width * fWidthScale / fTextSize, fTextSize, true);
67088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
67188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
67288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        if (fAnimate) {
67388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            fWidth += fDWidth;
67488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            if (fDWidth > 0 && fWidth > kWidthMax) {
67588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                fDWidth = -fDWidth;
67688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            } else if (fDWidth < 0 && fWidth < kWidthMin) {
67788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                fDWidth = -fDWidth;
67888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            }
67988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
68088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        setAsNeeded();
68104e4d08556750ff6be4576a4cd4925964c63376fcaryclark        if (fConicButton.fEnabled) {
68204e4d08556750ff6be4576a4cd4925964c63376fcaryclark            draw_control(canvas, fWeightControl, fWeight, 0, 5, "weight");
68304e4d08556750ff6be4576a4cd4925964c63376fcaryclark        }
68488651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        if (fArcButton.fEnabled) {
68588651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            draw_control(canvas, fRadiusControl, fRadius, 0, 500, "radius");
68688651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        }
687a76b7a3b04bc0e937921a1eb38d713c619e60e4ecaryclark#ifdef SK_DEBUG
68888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        draw_control(canvas, fErrorControl, gDebugStrokerError, kStrokerErrorMin, kStrokerErrorMax,
68988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                "error");
69088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark#endif
69188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        draw_control(canvas, fWidthControl, fWidth * fWidthScale, kWidthMin * fWidthScale,
69288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                kWidthMax * fWidthScale, "width");
69388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        draw_button(canvas, fQuadButton);
69488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        draw_button(canvas, fCubicButton);
69504e4d08556750ff6be4576a4cd4925964c63376fcaryclark        draw_button(canvas, fConicButton);
69688651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        draw_button(canvas, fArcButton);
69788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        draw_button(canvas, fRRectButton);
69804e4d08556750ff6be4576a4cd4925964c63376fcaryclark        draw_button(canvas, fCircleButton);
69988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        draw_button(canvas, fTextButton);
70096fcdcc219d2a0d3579719b84b28bede76efba64halcanary        this->inval(nullptr);
70188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    }
70288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
70388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    class MyClick : public Click {
70488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    public:
70588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        int fIndex;
70688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        MyClick(SkView* target, int index) : Click(target), fIndex(index) {}
70788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    };
70888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
70988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
71036352bf5e38f45a70ee4f4fc132a38048d38206dmtklein                                              unsigned modi) override {
71188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); ++i) {
71288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            if (hittest(fPts[i], x, y)) {
71388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                return new MyClick(this, (int)i);
71488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            }
71588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
71688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        const SkRect& rectPt = SkRect::MakeXYWH(x, y, 1, 1);
71704e4d08556750ff6be4576a4cd4925964c63376fcaryclark        if (fWeightControl.contains(rectPt)) {
71804e4d08556750ff6be4576a4cd4925964c63376fcaryclark            return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 1);
71904e4d08556750ff6be4576a4cd4925964c63376fcaryclark        }
72088651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        if (fRadiusControl.contains(rectPt)) {
72188651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 2);
72288651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        }
723a76b7a3b04bc0e937921a1eb38d713c619e60e4ecaryclark#ifdef SK_DEBUG
72488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        if (fErrorControl.contains(rectPt)) {
72588651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 3);
72688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
72788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark#endif
72888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        if (fWidthControl.contains(rectPt)) {
72988651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 4);
73088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
73188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        if (fCubicButton.fBounds.contains(rectPt)) {
73288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            fCubicButton.fEnabled ^= true;
73388651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 5);
73488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
73504e4d08556750ff6be4576a4cd4925964c63376fcaryclark        if (fConicButton.fBounds.contains(rectPt)) {
73604e4d08556750ff6be4576a4cd4925964c63376fcaryclark            fConicButton.fEnabled ^= true;
73788651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 6);
73804e4d08556750ff6be4576a4cd4925964c63376fcaryclark        }
73988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        if (fQuadButton.fBounds.contains(rectPt)) {
74088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            fQuadButton.fEnabled ^= true;
74188651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 7);
74288651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        }
74388651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        if (fArcButton.fBounds.contains(rectPt)) {
74488651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            fArcButton.fEnabled ^= true;
74588651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 8);
74688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
74788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        if (fRRectButton.fBounds.contains(rectPt)) {
74888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            fRRectButton.fEnabled ^= true;
74988651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 9);
75004e4d08556750ff6be4576a4cd4925964c63376fcaryclark        }
75104e4d08556750ff6be4576a4cd4925964c63376fcaryclark        if (fCircleButton.fBounds.contains(rectPt)) {
75204e4d08556750ff6be4576a4cd4925964c63376fcaryclark            bool wasEnabled = fCircleButton.fEnabled;
75304e4d08556750ff6be4576a4cd4925964c63376fcaryclark            fCircleButton.fEnabled = !fCircleButton.fFill;
75404e4d08556750ff6be4576a4cd4925964c63376fcaryclark            fCircleButton.fFill = wasEnabled && !fCircleButton.fFill;
75588651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 10);
75688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
75788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        if (fTextButton.fBounds.contains(rectPt)) {
75888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            fTextButton.fEnabled ^= true;
75988651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 11);
76088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
76188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        return this->INHERITED::onFindClickHandler(x, y, modi);
76288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    }
76388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
76488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    static SkScalar MapScreenYtoValue(int y, const SkRect& control, SkScalar min,
76588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            SkScalar max) {
76688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        return (SkIntToScalar(y) - control.fTop) / control.height() * (max - min) + min;
76788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    }
76888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
76936352bf5e38f45a70ee4f4fc132a38048d38206dmtklein    bool onClick(Click* click) override {
77088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        int index = ((MyClick*)click)->fIndex;
77188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        if (index < (int) SK_ARRAY_COUNT(fPts)) {
77288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            fPts[index].offset(SkIntToScalar(click->fICurr.fX - click->fIPrev.fX),
77388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                               SkIntToScalar(click->fICurr.fY - click->fIPrev.fY));
77496fcdcc219d2a0d3579719b84b28bede76efba64halcanary            this->inval(nullptr);
77504e4d08556750ff6be4576a4cd4925964c63376fcaryclark        } else if (index == (int) SK_ARRAY_COUNT(fPts) + 1) {
77604e4d08556750ff6be4576a4cd4925964c63376fcaryclark            fWeight = MapScreenYtoValue(click->fICurr.fY, fWeightControl, 0, 5);
77788651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        } else if (index == (int) SK_ARRAY_COUNT(fPts) + 2) {
77888651aeb551fb02003a7600679e8e7df8a589e7fcaryclark            fRadius = MapScreenYtoValue(click->fICurr.fY, fRadiusControl, 0, 500);
77988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
780a76b7a3b04bc0e937921a1eb38d713c619e60e4ecaryclark#ifdef SK_DEBUG
78188651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        else if (index == (int) SK_ARRAY_COUNT(fPts) + 3) {
78288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            gDebugStrokerError = SkTMax(FLT_EPSILON, MapScreenYtoValue(click->fICurr.fY,
78388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                    fErrorControl, kStrokerErrorMin, kStrokerErrorMax));
78488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            gDebugStrokerErrorSet = true;
78588c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
78688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark#endif
78788651aeb551fb02003a7600679e8e7df8a589e7fcaryclark        else if (index == (int) SK_ARRAY_COUNT(fPts) + 4) {
78888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            fWidth = SkTMax(FLT_EPSILON, MapScreenYtoValue(click->fICurr.fY, fWidthControl,
78988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark                    kWidthMin, kWidthMax));
79088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark            fAnimate = fWidth <= kWidthMin;
79188c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        }
79288c748aeea1cedfc8280d7f3da3d949ed01be177caryclark        return true;
79388c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    }
79488c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
79588c748aeea1cedfc8280d7f3da3d949ed01be177caryclarkprivate:
79688c748aeea1cedfc8280d7f3da3d949ed01be177caryclark    typedef SkView INHERITED;
79788c748aeea1cedfc8280d7f3da3d949ed01be177caryclark};
79888c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
79988c748aeea1cedfc8280d7f3da3d949ed01be177caryclark///////////////////////////////////////////////////////////////////////////////
80088c748aeea1cedfc8280d7f3da3d949ed01be177caryclark
80188c748aeea1cedfc8280d7f3da3d949ed01be177caryclarkstatic SkView* F2() { return new QuadStrokerView; }
80288c748aeea1cedfc8280d7f3da3d949ed01be177caryclarkstatic SkViewRegister gR2(F2);
803