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