1fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/*
2fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Copyright 2017 Google Inc.
3fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *
4fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Use of this source code is governed by a BSD-style license that can be
5fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * found in the LICENSE file.
6fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot */
7fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
8fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SampleCode.h"
9fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkCanvas.h"
10fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkCommandLineFlags.h"
11fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkDOM.h"
12fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkSVGDOM.h"
13fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkOSPath.h"
14fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPath.h"
15fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPicture.h"
16fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkStream.h"
17fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include <stack>
18fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
19fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/**
20fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * This is a simple utility designed to extract the paths from an SKP file and then isolate a single
21fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * one of them. Use the 'x' and 'X' keys to guide a binary search:
22fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *
23fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *   'x': Throw out half the paths.
24fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *   'X': Toggle which half gets tossed and which half is kept.
25fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *   'Z': Back up one level.
26fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *   'D': Dump the path.
27fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot */
28fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotclass PathFinderView : public SampleView, public SkCanvas {
29fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotpublic:
30fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    PathFinderView(const char name[] = nullptr)
31fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        : SkCanvas(4096, 4096, nullptr)
32fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fFilename(name) {
33fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkFILEStream stream(fFilename.c_str());
34fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!stream.isValid()) {
35fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkDebugf("invalid input file at \"%s\"\n", fFilename.c_str());
36fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return;
37fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
38fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (fFilename.endsWith(".svg")) {
39fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkDOM xml;
40fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (!xml.build(stream)) {
41fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkDebugf("XML parsing failed: \"%s\"\n", fFilename.c_str());
42fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return;
43fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
44fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            sk_sp<SkSVGDOM> svg = SkSVGDOM::MakeFromDOM(xml);
45fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (!svg) {
46fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkDebugf("couldn't load svg at \"%s\"\n", fFilename.c_str());
47fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return;
48fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
49fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            svg->setContainerSize(SkSize::Make(500, 500));
50fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            svg->render(this);
51fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else {
52fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            sk_sp<SkPicture> pic = SkPicture::MakeFromStream(&stream);
53fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (!pic) {
54fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkDebugf("couldn't load skp at \"%s\"\n", fFilename.c_str());
55fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return;
56fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
57fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            pic->playback(this);
58fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
59fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
60fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
61fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ~PathFinderView() override {}
62fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
63fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotprivate:
64fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Called through SkPicture::playback during construction.
65fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void onDrawPath(const SkPath& path, const SkPaint& paint) override {
66fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fPaths.push_back() = {path, paint, this->getTotalMatrix()};
67fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
68fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
69fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // overrides from SkEventSink
70fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool onQuery(SkEvent* evt) override {
71fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (SampleCode::TitleQ(*evt)) {
72fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkString name("PATHFINDER:");
73fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            const char* basename = strrchr(fFilename.c_str(), SkOSPath::SEPARATOR);
74fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            name.append(basename ? basename+1: fFilename.c_str());
75fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SampleCode::TitleR(evt, name.c_str());
76fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return true;
77fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
78fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkUnichar key;
79fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (SampleCode::CharQ(*evt, &key)) {
80fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (this->handleKeystroke(key)) {
81fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return true;
82fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
83fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
84fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return this->INHERITED::onQuery(evt);
85fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
86fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
87fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool handleKeystroke(SkUnichar key) {
88fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        switch (key) {
89fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case 'X':
90fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                if (!fTossedPaths.empty()) {
91fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    SkTSwap(fPaths, fTossedPaths);
92fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    if ('X' == fTrail.back()) {
93fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        fTrail.pop_back();
94fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    } else {
95fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        fTrail.push_back('X');
96fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    }
97fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
98fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return true;
99fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case 'x':
100fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                if (fPaths.count() > 1) {
101fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    int midpt = (fPaths.count() + 1) / 2;
102fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    fPathHistory.emplace(fPaths, fTossedPaths);
103fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    fTossedPaths.reset(fPaths.begin() + midpt, fPaths.count() - midpt);
104fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    fPaths.resize_back(midpt);
105fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    fTrail.push_back('x');
106fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
107fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return true;
108fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case 'Z': {
109fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                if (!fPathHistory.empty()) {
110fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    fPaths = fPathHistory.top().first;
111fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    fTossedPaths = fPathHistory.top().second;
112fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    fPathHistory.pop();
113fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    char ch;
114fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    do {
115fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        ch = fTrail.back();
116fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        fTrail.pop_back();
117fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    } while (ch != 'x');
118fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
119fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return true;
120fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
121fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case 'D':
122fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkDebugf("SampleApp --pathfinder %s", fFilename.c_str());
123fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                if (!fTrail.empty()) {
124fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    SkDebugf(" --keys ");
125fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    for (char ch : fTrail) {
126fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        SkDebugf("%c", ch);
127fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    }
128fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
129fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkDebugf("\n");
130fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                for (const FoundPath& foundPath : fPaths) {
131fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    foundPath.fPath.dump();
132fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
133fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return true;
134fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
135fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return false;
136fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
137fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
138fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void onDrawContent(SkCanvas* canvas) override {
139fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        for (const FoundPath& path : fPaths) {
140fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkAutoCanvasRestore acr(canvas, true);
141fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            canvas->concat(path.fViewMatrix);
142fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            canvas->drawPath(path.fPath, path.fPaint);
143fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
144fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
145fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
146fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    struct FoundPath {
147fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPath     fPath;
148fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint    fPaint;
149fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkMatrix   fViewMatrix;
150fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
151fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
152fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkString              fFilename;
153fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkTArray<FoundPath>   fPaths;
154fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkTArray<FoundPath>   fTossedPaths;
155fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkTArray<char>        fTrail;
156fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
157fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    std::stack<std::pair<SkTArray<FoundPath>, SkTArray<FoundPath>>> fPathHistory;
158fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
159fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    typedef SampleView INHERITED;
160fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot};
161fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
162fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSampleView* CreateSamplePathFinderView(const char filename[]) {
163fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return new PathFinderView(filename);
164fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
165