| /* | 
 |  * Copyright 2017 Google Inc. | 
 |  * | 
 |  * Use of this source code is governed by a BSD-style license that can be | 
 |  * found in the LICENSE file. | 
 |  */ | 
 |  | 
 | #include "SampleCode.h" | 
 | #include "SkCanvas.h" | 
 | #include "SkCommandLineFlags.h" | 
 | #include "SkOSPath.h" | 
 | #include "SkPath.h" | 
 | #include "SkPicture.h" | 
 | #include "SkStream.h" | 
 | #include <stack> | 
 |  | 
 | DEFINE_string(pathfinderTrail, "", "List of keystrokes to execute upon loading a pathfinder."); | 
 |  | 
 | /** | 
 |  * This is a simple utility designed to extract the paths from an SKP file and then isolate a single | 
 |  * one of them. Use the 'x' and 'X' keys to guide a binary search: | 
 |  * | 
 |  *   'x': Throw out half the paths. | 
 |  *   'X': Toggle which half gets tossed and which half is kept. | 
 |  *   'Z': Back up one level. | 
 |  *   'D': Dump the path. | 
 |  */ | 
 | class PathFinderView : public SampleView, public SkCanvas { | 
 | public: | 
 |     PathFinderView(const char name[] = nullptr) | 
 |         : SkCanvas(4096, 4096, nullptr) | 
 |         , fFilename(name) { | 
 |         SkFILEStream stream(fFilename.c_str()); | 
 |         if (!stream.isValid()) { | 
 |             SkDebugf("couldn't load picture at \"%s\"\n", fFilename.c_str()); | 
 |             return; | 
 |         } | 
 |         sk_sp<SkPicture> pic = SkPicture::MakeFromStream(&stream); | 
 |         if (!pic) { | 
 |             SkDebugf("couldn't load picture at \"%s\"\n", fFilename.c_str()); | 
 |             return; | 
 |         } | 
 |         pic->playback(this); | 
 |         for (int i = 0; i < FLAGS_pathfinderTrail.count(); ++i) { | 
 |             const char* key = FLAGS_pathfinderTrail[i]; | 
 |             while (*key) { | 
 |                 this->handleKeystroke(*key++); | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     ~PathFinderView() override {} | 
 |  | 
 | private: | 
 |     // Called through SkPicture::playback during construction. | 
 |     void onDrawPath(const SkPath& path, const SkPaint& paint) override { | 
 |         fPaths.push_back() = {path, paint, this->getTotalMatrix()}; | 
 |     } | 
 |  | 
 |     // overrides from SkEventSink | 
 |     bool onQuery(SkEvent* evt) override { | 
 |         if (SampleCode::TitleQ(*evt)) { | 
 |             SkString name("PATHFINDER:"); | 
 |             const char* basename = strrchr(fFilename.c_str(), SkOSPath::SEPARATOR); | 
 |             name.append(basename ? basename+1: fFilename.c_str()); | 
 |             SampleCode::TitleR(evt, name.c_str()); | 
 |             return true; | 
 |         } | 
 |         SkUnichar key; | 
 |         if (SampleCode::CharQ(*evt, &key)) { | 
 |             if (this->handleKeystroke(key)) { | 
 |                 return true; | 
 |             } | 
 |         } | 
 |         return this->INHERITED::onQuery(evt); | 
 |     } | 
 |  | 
 |     bool handleKeystroke(SkUnichar key) { | 
 |         switch (key) { | 
 |             case 'X': | 
 |                 if (!fTossedPaths.empty()) { | 
 |                     SkTSwap(fPaths, fTossedPaths); | 
 |                     if ('X' == fTrail.back()) { | 
 |                         fTrail.pop_back(); | 
 |                     } else { | 
 |                         fTrail.push_back('X'); | 
 |                     } | 
 |                     this->inval(nullptr); | 
 |                 } | 
 |                 return true; | 
 |             case 'x': | 
 |                 if (fPaths.count() > 1) { | 
 |                     int midpt = (fPaths.count() + 1) / 2; | 
 |                     fPathHistory.emplace(fPaths, fTossedPaths); | 
 |                     fTossedPaths.reset(fPaths.begin() + midpt, fPaths.count() - midpt); | 
 |                     fPaths.resize_back(midpt); | 
 |                     fTrail.push_back('x'); | 
 |                     this->inval(nullptr); | 
 |                 } | 
 |                 return true; | 
 |             case 'Z': { | 
 |                 if (!fPathHistory.empty()) { | 
 |                     fPaths = fPathHistory.top().first; | 
 |                     fTossedPaths = fPathHistory.top().second; | 
 |                     fPathHistory.pop(); | 
 |                     char ch; | 
 |                     do { | 
 |                         ch = fTrail.back(); | 
 |                         fTrail.pop_back(); | 
 |                     } while (ch != 'x'); | 
 |                     this->inval(nullptr); | 
 |                 } | 
 |                 return true; | 
 |             } | 
 |             case 'D': | 
 |                 SkDebugf("SampleApp --pathfinder %s", fFilename.c_str()); | 
 |                 if (!fTrail.empty()) { | 
 |                     SkDebugf(" --pathfinderTrail ", fFilename.c_str()); | 
 |                     for (char ch : fTrail) { | 
 |                         SkDebugf("%c", ch); | 
 |                     } | 
 |                 } | 
 |                 SkDebugf("\n"); | 
 |                 for (const FoundPath& foundPath : fPaths) { | 
 |                     foundPath.fPath.dump(); | 
 |                 } | 
 |                 return true; | 
 |         } | 
 |         return false; | 
 |     } | 
 |  | 
 |     void onDrawContent(SkCanvas* canvas) override { | 
 |         for (const FoundPath& path : fPaths) { | 
 |             SkAutoCanvasRestore acr(canvas, true); | 
 |             canvas->concat(path.fViewMatrix); | 
 |             canvas->drawPath(path.fPath, path.fPaint); | 
 |         } | 
 |     } | 
 |  | 
 |     struct FoundPath { | 
 |         SkPath     fPath; | 
 |         SkPaint    fPaint; | 
 |         SkMatrix   fViewMatrix; | 
 |     }; | 
 |  | 
 |     SkString              fFilename; | 
 |     SkTArray<FoundPath>   fPaths; | 
 |     SkTArray<FoundPath>   fTossedPaths; | 
 |     SkTArray<char>        fTrail; | 
 |  | 
 |     std::stack<std::pair<SkTArray<FoundPath>, SkTArray<FoundPath>>> fPathHistory; | 
 |  | 
 |     typedef SampleView INHERITED; | 
 | }; | 
 |  | 
 | SampleView* CreateSamplePathFinderView(const char filename[]) { | 
 |     return new PathFinderView(filename); | 
 | } |